wiki: add Use Cases (7) + Roadmap (8) — review scenario, ccs spawn, tickets

- 7-Use-Cases: flagship Opus<->gx00 code-review CONVERSATION, plus the 5
  mechanisms it needs: bridge_send trigger; discovery via bridge_sessions
  (roster+live); ccs-profile spawn ('ccs <profile> claude', guard via
  'ccs env <profile>' host allowlist); the ID contract (envelope: from/to/
  session/turn/corr/kind/body + kind vocabulary); persistent-reviewer lifecycle.
  Plus a use-case catalogue.
- 8-Roadmap: walking-skeleton-first 5 stages (gantt + table), consolidated tech
  stack (incl. ccs spawn + ccs env guard), tickets per stage, and detailed
  Stage-1 tickets CB-101..107 with acceptance + dependency graph.
- 2-Message-Server: envelope 'open question' now resolved -> links to Use Cases + CB-201
- _Sidebar + Home index: add chapters 7 and 8
All 4 new mermaid blocks validated (2 fixed for Note semicolon/quotes).
Dai Ha
2026-07-12 07:44:43 +02:00
parent 6b3c2a1d16
commit 9d7947c03a
5 changed files with 362 additions and 6 deletions
+6 -6
@@ -272,12 +272,12 @@ envelope" resolves to **a worker-side hook that runs our code at turn end**:
and best-effort parses the last assistant block (reuse AgentAPI's `msgfmt`). This is
lossy and is the reason the envelope path is preferred.
> **Open design question.** The envelope contract (fields, how the worker signals "blocked,
> need input" vs "done", how tool/diff artifacts are attached) is **not yet pinned down**.
> A `Stop`-hook only sees the transcript, so rich structure (e.g. a machine-readable result
> object) requires the worker to *emit* it deliberately — via a skill/slash-command it calls
> before finishing, or a convention the hook parses. Treat the envelope schema as a design
> deliverable for M2, not a solved detail.
> **Envelope contract — now specified.** A first-cut envelope (fields `from`/`to`/`session`/
> `turn`/`corr`/`kind`/`body`, and the `kind` verb vocabulary that tells a recipient what to do
> next) is defined in [Use Cases → The ID contract](7-Use-Cases#mechanism-4--the-id-contract-envelope).
> Landing it is **Stage 2** ([Roadmap](8-Roadmap), ticket `CB-201`). The MCP-mounted worker
> emits its reply via `bridge_reply` (structured); the `Stop`-hook envelope remains the fallback
> for a non-MCP worker. Still open: exactly how tool/diff artifacts attach to `review.reply`.
## Delivery gating & races (the injector is a single writer)
+190
@@ -0,0 +1,190 @@
# 7. Use Cases
Concrete scenarios on the finalized architecture ([sole gateway](1-Architecture) +
[MCP-unified](2-Message-Server) + ccs-spawned workers). The **flagship** is a code-review
*conversation* between the primary Opus and a worker running the **GX10 vLLM** model — it
exercises every mechanism the system needs (trigger, discovery, ccs spawn, the ID contract,
and worker lifecycle), so we design it in full, then catalogue the rest.
Everything below holds the invariants: the primary stays env-CLEAN on Pro/Max, the worker's
model/account come from a **ccs profile**, and both talk only to `bridged` over MCP.
## Flagship — a review conversation (Opus ↔ gx00 reviewer)
You're working in Opus on code in a workspace and want a second pair of eyes — an actual
back-and-forth **review conversation**, not a one-shot lint — with a cheap worker on the GX10
vLLM model. Opus never leaves its subscription; it just calls MCP tools.
```mermaid
sequenceDiagram
participant O as "Opus (primary, env CLEAN)"
participant B as "bridged (gateway)"
participant C as "ccs + herdr"
participant W as "worker · gx00-vllm"
O->>B: "bridge_sessions() — what workers can I use?"
B-->>O: "profiles:[gx00-vllm=DeepSeek, …] · sessions:[]"
O->>B: "bridge_send(to: reviewer@gx00-vllm, {kind: review.request, diff, focus})"
Note over B: "guard: ccs env gx00-vllm → base_url host on allowlist ✓"
B->>C: "spawn: ccs gx00-vllm claude (new herdr pane)"
C->>W: "worker Ready → inject review.request"
activate W
W->>B: "bridge_reply({kind: review.reply, findings:[…]})"
deactivate W
B-->>O: "tool result = findings"
Note over O: "reads findings, wants to dig into one"
O->>B: "bridge_send(session s_7f3a, {kind: question — why finding 3 high-sev})"
B->>W: "inject into the SAME reviewer pane (context still warm)"
activate W
W->>B: "bridge_reply({kind: answer, …})"
deactivate W
B-->>O: "tool result = answer"
Note over O,W: "same reviewer session reused across turns → the diff stays in its context"
```
*Figure: discovery → trigger → ccs-spawn (guarded) → structured reply → follow-up on the same
warm session. Every arrow from Opus is an MCP tool call to `bridged`; the worker's provider
routing lives entirely in its ccs profile.*
The rest of this page is the five mechanisms this scenario needs.
## Mechanism 1 — triggering a subtask (`bridge_send`)
Opus delegates with **one** tool call:
```jsonc
bridge_send({
"to": "reviewer@gx00-vllm", // role@profile, or a live session id
"kind": "review.request",
"body": { "workspace": "/repo", "base": "main", "head": "HEAD",
"focus": ["correctness","security"], "instructions": "…" },
"mode": "block" // block (default) → reply as tool result; async → ticket
})
```
`bridged` resolves the target (spawn-or-reuse, below), injects the turn into the worker's
herdr pane gated on `agent_status`, and — for `mode:"block"` — holds the call open until the
reply lands (worker `bridge_reply` or `agent_status=done`), returning it as the tool result.
Review turns are short, so they block; a long/detached job would use `mode:"async"` and come
back via idle-pane injection ([Mode 2](1-Architecture#traffic-two-modes-across-the-gateway)).
## Mechanism 2 — worker discovery (knowing your choices)
Opus shouldn't hard-code pane ids or guess what's available. `bridge_sessions()` returns both
what's **runnable** and what's **live**:
```jsonc
bridge_sessions() → {
"profiles": [ // spawnable = the ccs worker roster (Mechanism 3)
{ "name": "gx00-vllm", "model": "DeepSeek-V3", "host": "gx00.ltms.dev", "status": "available" },
{ "name": "ollama-local","model": "llama3.1", "host": "ollama.ltms.dev","status": "available" }
],
"sessions": [ // live worker sessions right now
{ "id": "s_7f3a", "role": "reviewer", "profile": "gx00-vllm", "agent_status": "idle" }
]
}
```
This is the "let Opus know its worker choices" surface: the catalogue is `bridged`'s configured
roster of ccs worker profiles, and the live list is what it's already running.
## Mechanism 3 — spawning via ccs profiles (the flexibility)
**A worker's identity *is* a ccs profile.** `ccs <profile> [claude-args…]` launches `claude`
with that profile's account and provider routing, so `bridged` never hand-assembles env — it
just picks a profile:
- **Config.** `bridged.yaml` lists worker profiles by name; each maps to a ccs profile
(`ccs api` profile pointing at GX10 vLLM / Ollama), an expected model, and an allowlisted
`base_url` host.
- **Spawn.** `bridged` tells herdr to open a pane and `send_text`: `ccs gx00-vllm claude`
(plus flags — workspace dir, an injected reviewer system prompt). No `ANTHROPIC_BASE_URL=…`
prefix; the profile carries it.
- **Guard (subscription boundary, in ccs terms).** Before spawning, `bridged` runs
`ccs env <profile>` and validates the **resolved** `ANTHROPIC_BASE_URL` host against the
off-subscription allowlist. A profile that resolves to `api.anthropic.com` (a subscription
profile) is **refused as a worker** — that would burn your quota. The primary Opus is *your*
session on *your* subscription profile; `bridged` never spawns it.
- **Swap = repoint.** Changing the reviewer's model is choosing a different ccs profile — no
`bridged` code change. Add a profile → it appears in `bridge_sessions().profiles`.
```mermaid
flowchart LR
CFG["bridged.yaml<br/>worker profiles"] --> PICK["pick profile<br/>gx00-vllm"]
PICK --> GUARD{"ccs env host<br/>on allowlist?"}
GUARD -->|"no (api.anthropic.com)"| REJ["refuse — would burn subscription"]
GUARD -->|"yes (gx00.ltms.dev)"| SPAWN["herdr: ccs gx00-vllm claude"]
SPAWN --> PANE["worker pane<br/>Ready"]
classDef ok fill:#2f855a,stroke:#22543d,color:#ffffff;
classDef bad fill:#b7791f,stroke:#7b341e,color:#ffffff;
class SPAWN,PANE ok
class REJ bad
```
*Figure: ccs profile selection is the spawn contract; `ccs env` is how the subscription guard
sees the resolved endpoint before committing.*
## Mechanism 4 — the ID contract (envelope)
Every message across the gateway is one envelope. It answers two questions each side needs:
**whose message is this** (`from`/`to`) and **what do I do next** (`kind`).
| Field | Meaning |
|---|---|
| `v` | envelope version (`1`) |
| `from` | sender identity — `primary:opus`, or `role@profile` / session id for a worker |
| `to` | recipient — `role@profile` or a live session id |
| `session` | `bridged` session id (the worker session), e.g. `s_7f3a` |
| `turn` | monotonic counter within the session |
| `corr` | correlation id (`session#turn`) — `bridged`'s rendezvous matches a reply to its request |
| `kind` | **verb.noun** telling the recipient what to do (vocabulary below) |
| `body` | kind-specific payload |
**`kind` vocabulary (the "what to do next"):**
| kind | direction | body |
|---|---|---|
| `review.request` | primary → worker | `{workspace, base, head\|diff, files?, focus[], instructions}` |
| `review.reply` | worker → primary | `{summary, findings:[{file,line,severity,issue,suggestion}], verdict}` |
| `question` / `answer` | either way | free-form follow-up tied to the same `session` |
| `ask` | worker → primary | worker-initiated blocker (needs a decision) — via `bridge_ask` |
| `ack` / `status` | control | delivery/liveness, no new turn |
The worker learns this contract from a **reviewer skill / `CLAUDE.md` snippet** injected at
spawn ("you are a reviewer; requests arrive as `review.request`; reply with `bridge_reply`
`kind: review.reply`"). So both ends know the sender and the required next action without a
held-open conversation — one request in, one structured reply out.
## Mechanism 5 — worker lifecycle
For a review conversation the reviewer is **persistent within a work session** so the diff
stays warm across follow-ups, and recyclable so it never outgrows its context window:
- **Spawn-on-demand** — first `review.request` for a workspace spawns the profile's worker.
- **Reuse** — subsequent turns (`question`, next-file review) target the same `session`; its
context carries the code under review.
- **Recycle (Ralph loop)** — on a context/idle cap `bridged` checkpoints (commit + `STATE.md`)
and respawns fresh — **not** `claude --resume`. See
[Architecture → Worker lifecycle](1-Architecture#worker-lifecycle--the-ralph-loop).
- **Drain** — an `idle_ttl` (e.g. 20 min idle) or workspace close tears the pane down.
Policy knobs in `bridged.yaml`: `idle_ttl`, `context_cap`, `max_workers_per_profile`.
## More use cases (catalogue)
Same machinery, different `kind`/lifecycle:
| Use case | Shape |
|---|---|
| **Delegated refactor / codegen** | `bridge_send(kind: task.request)`, blocking; worker edits + commits on a branch; reply = diff summary. Opus reviews/merges. |
| **Test writing / log triage** | Bulk, cheap, parallelizable — a local worker profile; fire several concurrently and reduce. |
| **Parallel multi-file review** | A **fleet** of workers, one per area, fan-out/gather — see [Team](6-Team). |
| **Perpetual worker** | Long-running across many Ralph recycles, state on disk; woken by async injection ([Mode 2](1-Architecture)). |
| **Event-bus automation** | A webhook hits `bridged`'s REST ingress → injects an idle worker → result back to Opus or a chat bridge. No human in the loop. |
## Related
- **[Architecture](1-Architecture)** — invariants, two modes, lifecycle state machine.
- **[Message Server](2-Message-Server)** — the `bridged` design these mechanisms live in.
- **[Roadmap](8-Roadmap)** — the staged plan + tickets that build this review scenario first.
- **[Team](6-Team)** — the fleet/orchestration layer above a single review.
+162
@@ -0,0 +1,162 @@
# 8. Roadmap & Delivery
A **walking-skeleton-first** plan: Stage 1 delivers the flagship
[review scenario](7-Use-Cases#flagship--a-review-conversation-opus--gx00-reviewer) end-to-end
(thin but real — Opus gets a review back from a gx00 worker over MCP), then later stages add
reply fidelity, the subscription guard, lifecycle, async, and hardening. This re-scopes the
[Message Server](2-Message-Server#build-plan-milestones) M0–M4 milestones around the scenario,
so there's something usable after every stage.
## Stages
```mermaid
gantt
title Indicative delivery sequence (relative durations, not committed dates)
dateFormat YYYY-MM-DD
axisFormat %b %d
section Skeleton
Stage 1 — review walking skeleton :s1, 2026-07-14, 10d
section Fidelity
Stage 2 — envelope + guard + reply :s2, after s1, 8d
section Scale
Stage 3 — lifecycle + discovery + fleet :s3, after s2, 10d
section Async
Stage 4 — async + durability + split :s4, after s3, 10d
section Harden
Stage 5 — auth · metrics · CI · systemd :s5, after s4, 8d
```
| Stage | Goal | Delivers (usable outcome) |
|---|---|---|
| **1 — Walking skeleton** | One review, happy path | Opus mounts `bridged` (MCP), calls `bridge_send` with a diff, gets a review back from a real `ccs gx00-vllm claude` worker. Single hardcoded profile, same host, no guard/lifecycle. |
| **2 — Contract + guard** | Trust the reply, trust the boundary | Structured [envelope](7-Use-Cases#mechanism-4--the-id-contract-envelope) + worker `bridge_reply`; reply rendezvous; subscription guard via `ccs env`; reviewer skill. |
| **3 — Lifecycle + discovery** | Reuse, recycle, choose | Session manager (spawn/reuse/recycle Ralph loop, `idle_ttl`); `bridge_sessions` roster+live; multiple profiles; `bridge_ask`. |
| **4 — Async + split-host** | Detached + cross-host | `mode:"async"` + idle-pane injection; internal queue (durability); split-host `Stop`-hook adapter that polls `bridged`. |
| **5 — Harden** | Production shape | Auth/TLS, `/metrics` + `/healthz`, mock-socket CI, systemd unit, per-session authz + audit. |
## Tech stack
Consolidated from [Message Server](2-Message-Server#proposed-tech-stack); the ccs pieces are new.
| Concern | Choice | Note |
|---|---|---|
| **Language** | **Go** | Single static binary → `scp`/systemd; goroutines fit socket + MCP + queue fan-in; reuse `agentapi` `msgfmt`. |
| **MCP server** | `mcp-go` / official Go SDK, streamable-HTTP | The SERVER-face contract both sides mount. |
| **herdr client** | Unix-socket NDJSON, id-correlated + event stream | Native herdr contract. |
| **Worker spawn** | **`ccs <profile> claude`** into a herdr pane | Profile = worker identity; no env-prefix. |
| **Subscription guard** | **`ccs env <profile>`** → resolved `base_url` host allowlist | Boundary check in ccs terms. |
| **Config** | YAML (`koanf`) + env | `bridged.yaml`: worker profiles, allowlist, bind addr, lifecycle knobs. |
| **Internal queue** *(Stage 4)* | Redis Streams (ack + visibility) | Below the gateway; embedded queue OK single-host. |
| **Observability** | `slog` + Prometheus `/metrics` + `/healthz` | Stage 5. |
| **Supervision** | systemd unit, ordered after herdr | Stage 5. |
| **Testing** | Mock herdr socket + golden transcripts; fake `ccs`/`claude` stubs | Deterministic CI, no TTY. |
## Tickets by stage (2–5)
Compact scope; expand into detailed tickets when a stage starts (as Stage 1 is below).
| Stage | Tickets |
|---|---|
| **2** | `CB-201` envelope schema + codec · `CB-202` worker `bridge_reply` tool + reviewer skill · `CB-203` reply rendezvous (corr match; resolve on reply *or* `done`) · `CB-204` subscription guard via `ccs env` + allowlist · `CB-205` blocked-worker path (`bridge_ask`) |
| **3** | `CB-301` session manager (spawn/reuse/recycle) · `CB-302` Ralph checkpoint (`STATE.md` + commit) · `CB-303` `idle_ttl`/`context_cap`/drain · `CB-304` `bridge_sessions` roster+live · `CB-305` multi-profile routing (`role@profile`) |
| **4** | `CB-401` `mode:"async"` + ticket · `CB-402` idle-pane injection delivery · `CB-403` internal queue (Redis Streams) · `CB-404` split-host `Stop`-hook adapter (polls `bridged`) · `CB-405` REST ingress for event-bus |
| **5** | `CB-501` bearer auth + TLS · `CB-502` `/metrics` + `/healthz` · `CB-503` mock-socket CI · `CB-504` systemd unit + ordered start · `CB-505` per-session authz + audit log |
## Stage 1 — detailed tickets
**Goal:** Opus, from its own subscription session, mounts `bridged` and gets a code review
back from a real `ccs gx00-vllm claude` worker — same host, one hardcoded profile, reply via a
pane scrape (envelope comes in Stage 2). This is the thinnest end-to-end vertical slice.
**Definition of done for the stage:** `CB-107` demo passes.
---
### CB-101 — herdr socket client
**Scope.** Connect to `~/.config/herdr/herdr.sock` (`HERDR_SOCKET_PATH`); implement
`Call(method, params) → result` (NDJSON, `id`-correlated) and `Subscribe(subs) → <-chan Event`
for `pane.agent_status_changed`. Probe `session.snapshot` on connect; fail fast on schema
mismatch.
**Acceptance.** Unit test against a **mock socket** replays a `workspace.create` round-trip and
one event; `session.snapshot` shape asserted.
**Deps.** none.
### CB-102 — spawn a worker via ccs profile
**Scope.** Given a profile name, open a herdr pane and `send_text` `ccs <profile> claude`
(+ workspace `cwd`); detect **Ready** via `output_matched` / `pane.read {source:"detection"}`
(prompt-ready), not an absent status.
**Acceptance.** Against a fake `ccs`+`claude` stub in a real herdr pane, the worker reaches
`Ready`; the resolved command is exactly `ccs gx00-vllm claude` (asserted from a spawn log).
**Deps.** CB-101.
### CB-103 — status-gated injector
**Scope.** Per-pane FIFO; `Deliver(pane, text)` sends `send_text` + `send_keys "enter"` **only**
when `agent_status ∈ {idle, blocked}`, else queues until the next idle event. Single writer per
pane; status-check + send serialized on the event goroutine (close the TOCTOU window).
**Acceptance.** A delivery issued while the pane is `working` lands only after the `idle` event;
two rapid deliveries never interleave (golden transcript).
**Deps.** CB-101.
### CB-104 — blocking `bridge_send` + reply capture
**Scope.** `bridge_send(to, body, {mode:"block"})` → resolve `to` to the (single, hardcoded)
worker pane → inject the request text → wait for `agent_status=done` → return
`pane.read {source:"recent-unwrapped"}` of the last assistant block as the tool result. (No
envelope yet; scrape is acceptable for Stage 1.)
**Acceptance.** A canned request produces a non-empty reply string derived from the pane; a
`working→done` transition unblocks the call; a timeout returns a typed "still working" error.
**Deps.** CB-102, CB-103.
### CB-105 — MCP server (SERVER face)
**Scope.** Streamable-HTTP MCP server exposing `bridge_send` and `bridge_status`; bind
`127.0.0.1:8080`. Provide the one-line mount: `claude mcp add --transport http bridge
http://127.0.0.1:8080/mcp`.
**Acceptance.** `claude mcp list` shows `bridge` connected from the primary; calling
`bridge_status` returns the worker's live `agent_status`.
**Deps.** CB-104.
### CB-106 — config + wiring
**Scope.** `bridged.yaml` load (`koanf`): one worker profile (`gx00-vllm` → ccs profile, model,
`base_url` host), bind address, workspace root. `slog` startup line logging the resolved worker
command (secrets redacted). Run as a foreground process (systemd deferred to Stage 5).
**Acceptance.** Bad/missing profile fails fast with a clear message; a valid config boots and
logs the resolved (redacted) spawn command.
**Deps.** none (parallel with CB-101).
### CB-107 — end-to-end review demo (stage gate)
**Scope.** Scripted demo: start herdr → start `bridged` → mount MCP on a primary `claude` →
from the primary, `bridge_send` a real diff with `kind:"review.request"` (body carried as text
for now) → assert a review comes back as the tool result. Document the exact steps in
[Setup](4-Setup).
**Acceptance.** The demo runs green on one host end-to-end; the worker is verifiably the
`ccs gx00-vllm` process (not the subscription); the primary's env has no `ANTHROPIC_BASE_URL`.
**Deps.** CB-105, CB-106.
```mermaid
flowchart LR
CB101["CB-101<br/>herdr client"] --> CB102["CB-102<br/>ccs spawn"]
CB102 --> CB103["CB-103<br/>injector"]
CB103 --> CB104["CB-104<br/>bridge_send"]
CB104 --> CB105["CB-105<br/>MCP server"]
CB106["CB-106<br/>config"] --> CB107
CB105 --> CB107["CB-107<br/>e2e demo (gate)"]
classDef gate fill:#2f855a,stroke:#22543d,color:#ffffff;
class CB107 gate
```
*Figure: Stage 1 dependency order. CB-106 runs in parallel; everything converges on the CB-107
end-to-end gate.*
## Open decisions (surface before/while building)
- **ccs worker profiles** — which existing ccs profiles (or new `ccs api` profiles) back the
`gx00-vllm` / local workers, and their exact `base_url` hosts for the allowlist.
- **Reviewer system prompt** — ship the reviewer skill as a `CLAUDE.md` snippet vs a
slash-command/skill the worker loads at spawn.
- **Envelope-as-text (Stage 1) → structured (Stage 2)** — confirm the Stage-1 shortcut (request
body inlined as prompt text, reply scraped) is acceptable before the envelope lands.
## Related
- **[Use Cases](7-Use-Cases)** — the review scenario and the five mechanisms these stages build.
- **[Message Server](2-Message-Server)** — component design the tickets implement.
- **[Setup](4-Setup)** · **[Operations](5-Operations)** — bring-up and day-2, fleshed out as stages land.
+2
@@ -75,6 +75,8 @@ Read in order (the sidebar mirrors this):
4. **[Setup](4-Setup)** — running herdr + `bridged` + a worker pointed at `ollama.ltms.dev`
5. **[Operations](5-Operations)** — health, restart, model swaps, troubleshooting
6. **[Team](6-Team)** — team-lead orchestrating a mixed Claude + local-LLM worker fleet
7. **[Use Cases](7-Use-Cases)** — flagship code-review conversation (Opus ↔ gx00 worker) + the five mechanisms
8. **[Roadmap](8-Roadmap)** — walking-skeleton-first stages, tech stack, and tickets (Stage 1 detailed)
## Status
+2
@@ -10,6 +10,8 @@
4. [Setup](4-Setup) — bring-up
5. [Operations](5-Operations) — day-2 runbook
6. [Team](6-Team) — orchestrating a mixed fleet
7. [Use Cases](7-Use-Cases) — the review scenario + mechanisms
8. [Roadmap](8-Roadmap) — stages, tech stack, tickets
---
🟢 herdr-centric `bridged` · AgentAPI = fallback