harness: remove deprecated manager-side request_spawn surface (closes #442)

This commit is contained in:
damocles 2026-05-27 18:58:04 +02:00
commit dca62fda40
8 changed files with 56 additions and 124 deletions

View file

@ -120,7 +120,6 @@ can't:
|---|---|---|
| `RequestInitConfig` | seed an agent's proposed config repo | **topology** — descendants only |
| `RequestApplyCommit` | submit a commit sha for operator approval | **topology** — descendants only |
| `RequestSpawn` (deprecated) | shortcut for spawn | **topology** — descendants only |
| `Kill` / `Start` / `Restart` / `Update` | container lifecycle on an existing agent | **topology** — descendants only |
| `RequestUpdateMetaInputs` | bump meta `flake.lock` | **per-agent cap** (root-only today; a future "let coder bump its own input" might grant it) |
| `GetLogs` | journalctl scrape of a sub-agent | **topology** — descendants only |

View file

@ -294,12 +294,13 @@ meta's.
on operator approve, hive-c0re seeds the proposed config repo
with a default `agent.nix` template and sends the manager a
`HelperEvent::ConfigReady { agent }`. The manager then edits
`agent.nix`, commits the changes, and calls `request_spawn`.
Fails if a proposed repo for this name already exists.
- `request_spawn(name)` — second step of a two-step spawn. Queues
a Spawn approval; requires the proposed config repo to exist
(from a prior approved `request_init_config`). Operator approves
on the dashboard to create the container.
`agent.nix`, commits the changes, and calls `request_apply_commit`
with the commit sha — the first ApplyCommit on a freshly-init'd
config creates the container. Fails if a proposed repo for this
name already exists. (The pre-#442 path through a separate
manager-side `request_spawn` was removed; operator can still
direct-spawn an empty agent from the dashboard's `◆ R3QU3ST SP4WN`
button which routes via `HostRequest::RequestSpawn`.)
- `kill(name)` — graceful stop. No approval required.
- `start(name)` — start a stopped sub-agent. No approval.
- `restart(name)` — stop + start. No approval.
@ -375,9 +376,9 @@ meta's.
The boundary: lifecycle ops on *existing* sub-agents
(`kill`/`start`/`restart`) are at the manager's discretion — no
operator approval. Creating a new agent (`request_spawn`) and
changing any agent's config (`request_apply_commit`) still go
through the approval queue.
operator approval. Creating a new agent (`request_init_config` →
`request_apply_commit` for the first sha) and changing any agent's
config (`request_apply_commit`) still go through the approval queue.
### Authoritative state

View file

@ -1056,7 +1056,6 @@ window.marked = marked;
if (input.max != null) parts.push('max ' + input.max);
return short + (parts.length ? ' ' + parts.join(' · ') : '()');
}
case 'mcp__hyperhive__request_spawn': return short + ' ' + (input.name || '');
case 'mcp__hyperhive__kill': return short + ' ' + (input.name || '');
case 'mcp__hyperhive__request_apply_commit':
return short + ' ' + (input.agent || '') + ' @ ' + (input.commit_ref || '').slice(0, 12);

View file

@ -36,8 +36,8 @@ enum Cmd {
},
/// Run the manager MCP server on stdio. Spawned by claude via
/// `--mcp-config`; same shape as `hive-ag3nt mcp` but with the
/// manager tool surface (`request_spawn`, `kill`, `start`, `restart`,
/// `request_apply_commit`, `ask`, `answer`, `remind`).
/// manager tool surface (`request_init_config`, `request_apply_commit`,
/// `kill`, `start`, `restart`, `ask`, `answer`, `remind`, …).
Mcp,
}

View file

@ -10,8 +10,8 @@
//!
//! Two server flavors:
//! - `AgentServer` — sub-agent tools (`send`, `recv`).
//! - `ManagerServer` — agent tools + lifecycle (`request_spawn`, `kill`,
//! `request_apply_commit`).
//! - `ManagerServer` — agent tools + lifecycle (`kill`,
//! `request_init_config`, `request_apply_commit`).
//!
//! Both go through the same `run_tool_envelope` helper so logging + status
//! line stay uniform.
@ -792,26 +792,16 @@ pub async fn serve_manager_stdio(socket: PathBuf) -> Result<()> {
pub struct RequestInitConfigArgs {
/// New sub-agent name (≤9 chars). Queues an `InitConfig` approval; on
/// approval hive-c0re seeds the proposed config repo at
/// `/agents/<name>/config/agent.nix` with the default template.
/// After the approval the manager can edit and commit the config before
/// calling `request_spawn`.
/// `/agents/<name>/config/agent.nix` with the default template. After
/// the approval the manager edits + commits the config and calls
/// `request_apply_commit` to pin the customised sha for the container's
/// first build.
pub name: String,
/// Optional description shown on the dashboard approval card.
#[serde(default)]
pub description: Option<String>,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct RequestSpawnArgs {
/// New sub-agent name (≤9 chars). Queues a Spawn approval; the
/// operator approves on the dashboard before the container is created.
pub name: String,
/// Optional description shown on the dashboard approval card so the
/// operator knows what the new agent is for without a separate message.
#[serde(default)]
pub description: Option<String>,
}
#[derive(Debug, serde::Deserialize, schemars::JsonSchema)]
pub struct KillArgs {
/// Sub-agent name (without the `h-` container prefix).
@ -1121,8 +1111,9 @@ impl ManagerServer {
repo and queue an InitConfig approval. On operator approval hive-c0re seeds \
`/agents/<name>/config/agent.nix` with the default template so the manager can \
customise it before spawning. After the ConfigReady helper event arrives, edit \
agent.nix, commit the changes, then call `request_spawn`. Fails if a config repo \
for this name already exists (use `request_apply_commit` to update an existing agent)."
agent.nix, commit the changes, then call `request_apply_commit` with the commit \
sha that's what creates the container. Fails if a config repo for this name \
already exists (use `request_apply_commit` directly to update an existing agent)."
)]
async fn request_init_config(
&self,
@ -1149,34 +1140,6 @@ impl ManagerServer {
.await
}
#[tool(
description = "Step 2 of 2 for creating a new agent: queue a Spawn approval after \
the config has been initialised and customised via `request_init_config`. Requires \
a prior approved InitConfig so the manager can review and edit agent.nix first. \
Fails if no proposed config repo exists for the given name."
)]
async fn request_spawn(&self, Parameters(args): Parameters<RequestSpawnArgs>) -> String {
let log = format!("{args:?}");
let name = args.name.clone();
run_tool_envelope("request_spawn", log, async move {
let (resp, retries) = self
.dispatch(hive_sh4re::ManagerRequest::RequestSpawn {
name: args.name,
description: args.description,
})
.await;
annotate_retries(
format_ack(
resp,
"request_spawn",
format!("spawn approval queued for {name}"),
),
retries,
)
})
.await
}
#[tool(
description = "Stop a sub-agent container (graceful). The state dir is kept; \
recreating reuses prior config + Claude credentials. No approval required."
@ -1711,10 +1674,12 @@ impl ManagerServer {
#[tool_handler(
instructions = "You are the hyperhive manager (hm1nd). You coordinate sub-agents and \
relay between them and the operator. Use `send` to talk to agents/operator, `recv` \
to drain your inbox. Privileged: `request_init_config` + `request_spawn` (two-step \
new agent creation - init config first, customise agent.nix, then spawn, both \
gated on operator approval), `kill` (graceful stop), `request_apply_commit` (config change for \
any agent including yourself), `ask` (structured question to the operator or a \
to drain your inbox. Privileged: `request_init_config` (step 1 of new-agent \
creation seeds the proposed config repo so you can customise agent.nix; \
operator-approved), `kill` (graceful stop), `request_apply_commit` (config \
change for any agent including yourself also doubles as step 2 of new-agent \
creation: the first ApplyCommit on a freshly-init'd config creates the \
container), `ask` (structured question to the operator or a \
sub-agent non-blocking, answer arrives later as a `question_answered` event), \
`answer` (respond to a `question_asked` event directed at you), \
`get_loose_ends` (hive-wide loose ends pending approvals + unanswered \
@ -1771,7 +1736,6 @@ pub fn allowed_mcp_tools(flavor: Flavor) -> Vec<String> {
"send",
"recv",
"request_init_config",
"request_spawn",
"kill",
"start",
"restart",

View file

@ -203,34 +203,6 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc<Coordinator>) -> ManagerResp
},
}
}
ManagerRequest::RequestSpawn { name, description } => {
tracing::info!(%name, "manager: request_spawn");
let proposed_dir = crate::coordinator::Coordinator::agent_proposed_dir(name);
if !proposed_dir.join(".git").exists() {
return ManagerResponse::Err {
message: format!(
"no proposed config repo found for '{name}' - \
call request_init_config first to initialise and customise \
the config before spawning"
),
};
}
match coord.approvals.submit_kind(
name,
hive_sh4re::ApprovalKind::Spawn,
"",
description.as_deref(),
) {
Ok(id) => {
tracing::info!(%id, %name, "spawn approval queued");
coord.emit_approval_added(id, name, "spawn", None, None, description.clone());
ManagerResponse::Ok
}
Err(e) => ManagerResponse::Err {
message: format!("{e:#}"),
},
}
}
ManagerRequest::Kill { name } => {
tracing::info!(%name, "manager: kill");
if name == crate::lifecycle::MANAGER_NAME {

View file

@ -568,16 +568,19 @@ async fn dispatch(
crate::actions::run_approval_spawn(coord, Some(entry.id), approval_id).await
}
(QueueKind::Spawn, None) => {
// No non-approval Spawn caller today. The variant exists so
// operator-triggered `RequestSpawn` (deprecated) and the
// future direct-spawn admin path can route through here
// without a wire change.
tracing::debug!(
id = entry.id,
agent = %entry.agent,
"rebuild_queue: Spawn entry without approval_id is a no-op"
);
Ok(())
// Unreachable today: every Spawn entry is born from an
// approval (HostRequest::RequestSpawn → submit_kind →
// approve → enqueue with approval_id). The manager-side
// `RequestSpawn` surface that used to bypass approvals
// was removed in #442; if a future direct-spawn admin
// path needs to skip the approval ride it should wire
// its own action call rather than route through here.
anyhow::bail!(
"rebuild_queue: Spawn entry id={} agent={} arrived without an approval_id — \
nothing should enqueue this shape today",
entry.id,
entry.agent,
)
}
(QueueKind::Destroy, _) => {
// Reserved for future `destroy --purge` integration.

View file

@ -17,10 +17,14 @@ pub enum HostRequest {
/// approval queue intentionally so test scripts and one-off recoveries
/// don't need a separate approve step.
Spawn { name: String },
/// Submit a spawn request for the user to approve. On approval the host
/// creates and starts the container. Mirrors the manager's
/// `RequestSpawn` — exposed on the admin socket so the dashboard and CLI
/// can also queue spawns through the approval flow.
/// Submit a spawn request for the operator to approve. On approval the
/// host creates and starts the container with the default `agent.nix`
/// template. The dashboard's `◆ R3QU3ST SP4WN` button and the
/// `hive-c0re request-spawn` CLI both go through this. The
/// previously-mirrored manager-side `RequestSpawn` was removed
/// (#442) — managers now go through the two-step `request_init_config`
/// + `request_apply_commit` flow so the spawn captures the manager's
/// customised config.
RequestSpawn { name: String },
/// Stop a managed container (graceful).
Kill { name: String },
@ -107,12 +111,16 @@ pub enum ApprovalKind {
#[default]
ApplyCommit,
/// Create + start a new sub-agent container with the given name.
/// Used today by the host admin socket / dashboard "request spawn"
/// button; the manager-side `RequestSpawn` surface was removed in
/// favour of the two-step `InitConfig` → `ApplyCommit` flow (#442).
Spawn,
/// Initialise a new agent's proposed config repo so the manager can
/// customise it before submitting a `RequestSpawn`. On approval
/// hive-c0re seeds `proposed/<name>/` with the default `agent.nix`
/// template but does NOT create the container - that requires a
/// subsequent `RequestSpawn` approval.
/// customise it before submitting a `request_apply_commit`. On
/// approval hive-c0re seeds `proposed/<name>/` with the default
/// `agent.nix` template but does NOT create the container — that
/// requires a subsequent `ApplyCommit` approval pinning the
/// customised config sha.
InitConfig,
/// Run `nix flake update [inputs...]` on the meta flake and commit
/// the resulting lock changes. The `commit_ref` field stores the
@ -767,20 +775,6 @@ pub enum ManagerRequest {
#[serde(default, skip_serializing_if = "Option::is_none")]
description: Option<String>,
},
/// Submit a spawn request for the user to approve. On approval the host
/// creates and starts the container. Requires a prior approved
/// `request_init_config` so the manager can customise `agent.nix` first.
/// Fails if the proposed config repo for this name does not exist yet.
///
/// Deprecated: prefer `request_apply_commit` after `config_ready` —
/// it pins the exact commit sha, handles both first-time spawns and
/// subsequent rebuilds, and always reflects the committed config.
RequestSpawn {
name: String,
/// Optional description shown on the dashboard approval card.
#[serde(default, skip_serializing_if = "Option::is_none")]
description: Option<String>,
},
/// Stop a sub-agent (graceful).
Kill {
name: String,