CB-102 decided: native agent.* worker spawn (env-injected); roadmap build status
+23
-6
@@ -226,11 +226,27 @@ primary out of herdr — see [Deployment model](#deployment-model).)*
|
||||
> `workspace.list`/`pane.list` to enumerate/re-attach). Crucially, herdr 0.7.0 exposes a
|
||||
> **native `agent.*` namespace** — `agent.start`, `agent.send`, `agent.read`, `agent.list`,
|
||||
> `agent.get`, `agent.focus`, plus `server.agent_manifests` and `pane.report_agent` — so herdr
|
||||
> already models "agents", not just panes. **Open opportunity:** `bridged`'s south side may use
|
||||
> `agent.start`/`agent.send`/`agent.list` directly instead of the "create pane + `send_text` a
|
||||
> launch line" workaround below. Spike this in **Stage 1 (CB-102)** and prefer it if it carries
|
||||
> env/model cleanly; the pane-based path stays the documented fallback. (Also available:
|
||||
> `worktree.*` for git-isolated workers.)
|
||||
> already models "agents", not just panes. (Also available: `worktree.*` for git-isolated workers.)
|
||||
>
|
||||
> **CB-102 — decided: `bridged` drives the native `agent.*` path** (the pane + `send_text`
|
||||
> workaround below is the documented fallback only). The spike proved against the live daemon that
|
||||
> `agent.start` accepts a **first-class `env` map** that reaches the process environment — so a
|
||||
> worker's `ANTHROPIC_BASE_URL` is injected cleanly and guard-checked, with no shell-prefix
|
||||
> parsing and `bridged`'s own env untouched. herdr also tracks each worker's **Claude session
|
||||
> UUID** (`agent_session.value`), grounding the ID contract in herdr's own identity. Observed
|
||||
> `agent.*` schema:
|
||||
>
|
||||
> | Call | Params | Returns |
|
||||
> |---|---|---|
|
||||
> | `agent.start` | `{name, argv:[...], env:{...}}` | `{agent:{terminal_id, pane_id, workspace_id, agent_status}}` |
|
||||
> | `agent.send` | `{target, text}` | ack |
|
||||
> | `agent.read` | `{target, source}` — `source ∈ visible\|recent\|recent_unwrapped\|detection` | `{read:{text}}` |
|
||||
> | `agent.get` | `{target}` | `{agent:{…, agent_session:{value:<uuid>}, agent_status}}` |
|
||||
> | `agent.list` | `{}` | `{agents:[…]}` — discovery, each with its session UUID |
|
||||
>
|
||||
> `target` is the `terminal_id`. There is **no `agent.stop`** — tear a worker down with
|
||||
> `pane.close {pane_id}`. `agent_status ∈ idle\|working\|blocked\|unknown` drives the
|
||||
> status-gated injector (inject only when idle/blocked).
|
||||
>
|
||||
> **Two wire facts the Stage-1 client (CB-101) pinned by contract test — both bit the first
|
||||
> build:** (1) the request `id` **must be a JSON string** — an integer id is rejected with
|
||||
@@ -239,7 +255,8 @@ primary out of herdr — see [Deployment model](#deployment-model).)*
|
||||
> (open → one frame → one line → close), which as a bonus needs no locking. A long-lived socket is
|
||||
> only for the streaming `events.subscribe` path, not request/response.
|
||||
|
||||
The pane-based control path (the fallback, and what the examples below use):
|
||||
The pane-based control path — the **fallback** now that CB-102 chose `agent.*`; shown here for
|
||||
reference and for herdr builds without the `agent.*` namespace:
|
||||
|
||||
```jsonc
|
||||
// spawn: create a pane, then launch the worker in its shell (env stays worker-only)
|
||||
|
||||
+27
-10
@@ -122,11 +122,22 @@ Compact scope; expand into detailed tickets when a stage starts (as Stage 1 is b
|
||||
## 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.
|
||||
back from a real worker spawned under the `ccs ltms-local` profile (routing to the gx00 vLLM at
|
||||
`http://gx00.gw:8000`) — same host, one hardcoded profile, reply via a pane/agent read (envelope
|
||||
comes in Stage 2). This is the thinnest end-to-end vertical slice.
|
||||
|
||||
**Definition of done for the stage:** `CB-107` demo passes.
|
||||
|
||||
> **Build status (in `bridged/`, Maven · Java 25 · 25 tests green).**
|
||||
> ✅ **CB-101** herdr client — connection-per-call, contract-tested vs live 0.7.0.
|
||||
> ✅ **CB-102** worker spawn — native `agent.*` with env injection, guard-checked, live-verified.
|
||||
> ✅ **CB-106** config + logging — Jackson YAML + Logback.
|
||||
> 🟡 **CB-104** REST core — `GET /healthz`, `/sessions`, `/agents`, `POST/DELETE /workers` done;
|
||||
> the **blocking `POST /sessions/{id}/message` + reply capture** is the remaining piece.
|
||||
> ⬜ **CB-103** status-gated injector · **CB-105** MCP adapter · **CB-107** e2e demo gate.
|
||||
> Two herdr wire facts pinned by contract test: **string ids**, and **one request per
|
||||
> connection**. `agent.start` env is the subscription-safe injection point (no shell prefix).
|
||||
|
||||
---
|
||||
|
||||
### CB-101 — herdr socket client
|
||||
@@ -141,14 +152,20 @@ and one event; a **contract test vs the real herdr** asserts `ping.protocol == 1
|
||||
`workspace.list`/`pane.list` shapes.
|
||||
**Deps.** none.
|
||||
|
||||
### CB-102 — spawn a worker via ccs profile
|
||||
**Scope.** First **spike herdr's native `agent.start`/`agent.send`/`agent.list`** (0.7.0 has
|
||||
them) — if they carry the `ccs <profile> claude` command + env/model cleanly, use them. Else
|
||||
fall back to the documented path: open a herdr pane and `send_text` `ccs <profile> claude`
|
||||
(+ workspace `cwd`), detecting **Ready** via `pane.wait_for_output` / `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).
|
||||
### CB-102 — spawn a worker (native `agent.*`) ✅
|
||||
**Decided (spike done).** herdr's `agent.start {name, argv, env}` carries the worker command and
|
||||
a **first-class `env` map that reaches the process** (proven via `agent.read`), so `bridged`
|
||||
injects `ANTHROPIC_BASE_URL` there — guard-checked before the call — with no shell prefix and its
|
||||
own env untouched. Chosen over the pane + `send_text` fallback. herdr tracks each worker's Claude
|
||||
**session UUID** (`agent.list`/`agent.get`), which grounds the ID contract. Teardown is
|
||||
`pane.close` (no `agent.stop`).
|
||||
**Built.** `AgentControl` (start/send/read/get/status/list/close) + `WorkerService` (builds env
|
||||
from config, `assertWorker` **before** any herdr call) + REST `POST /workers`, `GET /agents`,
|
||||
`DELETE /workers/{paneId}`.
|
||||
**Acceptance (met).** Unit: `POST /workers` with an off-allowlist base_url → **403 and herdr is
|
||||
never touched**; a good base_url → `agent.start` env carries `ANTHROPIC_BASE_URL`. Contract: a
|
||||
live probe spawn proves env propagation and cleans up its pane. (Real `ccs ltms-local claude`
|
||||
worker spawn is exercised by the CB-107 demo.)
|
||||
**Deps.** CB-101.
|
||||
|
||||
### CB-103 — status-gated injector
|
||||
|
||||
Reference in New Issue
Block a user