From dca62fda4075693e574ea021c36bc96eaae982c2 Mon Sep 17 00:00:00 2001 From: damocles Date: Wed, 27 May 2026 18:58:04 +0200 Subject: [PATCH] harness: remove deprecated manager-side request_spawn surface (closes #442) --- docs/agent-hierarchy.md | 1 - docs/turn-loop.md | 19 +++++---- frontend/packages/agent/src/app.js | 1 - hive-ag3nt/src/bin/hive-m1nd.rs | 4 +- hive-ag3nt/src/mcp.rs | 66 +++++++----------------------- hive-c0re/src/manager_server.rs | 28 ------------- hive-c0re/src/rebuild_queue.rs | 23 ++++++----- hive-sh4re/src/lib.rs | 38 ++++++++--------- 8 files changed, 56 insertions(+), 124 deletions(-) diff --git a/docs/agent-hierarchy.md b/docs/agent-hierarchy.md index ee31411c..dc3d3b7c 100644 --- a/docs/agent-hierarchy.md +++ b/docs/agent-hierarchy.md @@ -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 | diff --git a/docs/turn-loop.md b/docs/turn-loop.md index 4090dd1f..339137ca 100644 --- a/docs/turn-loop.md +++ b/docs/turn-loop.md @@ -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 diff --git a/frontend/packages/agent/src/app.js b/frontend/packages/agent/src/app.js index 890b0f97..d82ecce1 100644 --- a/frontend/packages/agent/src/app.js +++ b/frontend/packages/agent/src/app.js @@ -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); diff --git a/hive-ag3nt/src/bin/hive-m1nd.rs b/hive-ag3nt/src/bin/hive-m1nd.rs index b429daba..c9e5e154 100644 --- a/hive-ag3nt/src/bin/hive-m1nd.rs +++ b/hive-ag3nt/src/bin/hive-m1nd.rs @@ -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, } diff --git a/hive-ag3nt/src/mcp.rs b/hive-ag3nt/src/mcp.rs index c77721bc..4cbafcde 100644 --- a/hive-ag3nt/src/mcp.rs +++ b/hive-ag3nt/src/mcp.rs @@ -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//config/agent.nix` with the default template. - /// After the approval the manager can edit and commit the config before - /// calling `request_spawn`. + /// `/agents//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, } -#[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, -} - #[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//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) -> 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 { "send", "recv", "request_init_config", - "request_spawn", "kill", "start", "restart", diff --git a/hive-c0re/src/manager_server.rs b/hive-c0re/src/manager_server.rs index 303d69f1..b23cbf40 100644 --- a/hive-c0re/src/manager_server.rs +++ b/hive-c0re/src/manager_server.rs @@ -203,34 +203,6 @@ async fn dispatch(req: &ManagerRequest, coord: &Arc) -> 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 { diff --git a/hive-c0re/src/rebuild_queue.rs b/hive-c0re/src/rebuild_queue.rs index abbcdd08..35a35f72 100644 --- a/hive-c0re/src/rebuild_queue.rs +++ b/hive-c0re/src/rebuild_queue.rs @@ -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. diff --git a/hive-sh4re/src/lib.rs b/hive-sh4re/src/lib.rs index d7e447db..8ee3e12c 100644 --- a/hive-sh4re/src/lib.rs +++ b/hive-sh4re/src/lib.rs @@ -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//` 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//` 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, }, - /// 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, - }, /// Stop a sub-agent (graceful). Kill { name: String,