From ff31f1b7522fbc9d238daa11def5dc354f91911e Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sun, 25 Oct 2026 09:34:01 +0000 Subject: [PATCH 1/6] feat(scaffold): split hive roles, seats and desks into separate templates The hive template now ships agents.json and desks.json alongside hive.json, and the scaffold writes all three into the loop folder. The roles, seats and desks blocks were moved out of hive.json so each concern lives in its own file. Auto-committed-on: cuttlefish Co-authored-by: Medulla --- crates/supermath-mcp/src/cli/scaffold/mod.rs | 10 +- .../supermath-mcp/template/hive/agents.json | 123 ++++++++++++++++ crates/supermath-mcp/template/hive/desks.json | 15 ++ crates/supermath-mcp/template/hive/hive.json | 134 ------------------ 4 files changed, 147 insertions(+), 135 deletions(-) create mode 100644 crates/supermath-mcp/template/hive/agents.json create mode 100644 crates/supermath-mcp/template/hive/desks.json diff --git a/crates/supermath-mcp/src/cli/scaffold/mod.rs b/crates/supermath-mcp/src/cli/scaffold/mod.rs index 61969f9..624dcbf 100644 --- a/crates/supermath-mcp/src/cli/scaffold/mod.rs +++ b/crates/supermath-mcp/src/cli/scaffold/mod.rs @@ -32,13 +32,21 @@ use super::hive::{Program, declarations}; use crate::error::{Error, Result}; /// The template, as (path in the loop folder, text) pairs. -const TEMPLATE: [(&str, &str); 17] = [ +const TEMPLATE: [(&str, &str); 19] = [ ("rsi.json", include_str!("../../../template/rsi.json")), ("brief.md", include_str!("../../../template/brief.md")), ( "hive/hive.json", include_str!("../../../template/hive/hive.json"), ), + ( + "hive/agents.json", + include_str!("../../../template/hive/agents.json"), + ), + ( + "hive/desks.json", + include_str!("../../../template/hive/desks.json"), + ), ( "hive/constitution.json", include_str!("../../../template/hive/constitution.json"), diff --git a/crates/supermath-mcp/template/hive/agents.json b/crates/supermath-mcp/template/hive/agents.json new file mode 100644 index 0000000..56d6d11 --- /dev/null +++ b/crates/supermath-mcp/template/hive/agents.json @@ -0,0 +1,123 @@ +{ + "roles": [ + { + "id": "lead", + "charter": "roles/lead.md" + }, + { + "id": "prover", + "charter": "roles/prover.md" + }, + { + "id": "refuter", + "charter": "roles/refuter.md" + } + ], + "seats": [ + { + "id": "lead", + "role": "lead", + "prompt": "seats/lead.md", + "template": "openhuman", + "config": { + "action_dir": "{incumbent}", + "max_iterations": 30 + }, + "tools": { + "allow": [ + "file_read", + "grep", + "glob", + "list", + "read_diff" + ], + "deny": [ + "wait", + "wait_loop", + "spawn_subagent", + "spawn_async_subagent", + "spawn_parallel_agents", + "web_fetch", + "shell" + ] + }, + "toolpacks": [ + "literature", + "ledger-read" + ] + }, + { + "id": "prover", + "role": "prover", + "prompt": "seats/prover.md", + "template": "openhuman", + "config": { + "action_dir": "{candidate}", + "max_iterations": 60 + }, + "tools": { + "allow": [ + "file_read", + "grep", + "glob", + "list", + "read_diff", + "file_write", + "edit", + "apply_patch" + ], + "deny": [ + "wait", + "wait_loop", + "spawn_subagent", + "spawn_async_subagent", + "spawn_parallel_agents", + "web_fetch", + "shell" + ] + }, + "toolpacks": [ + "lean", + "literature", + "ledger" + ] + }, + { + "id": "refuter", + "role": "refuter", + "prompt": "seats/refuter.md", + "template": "openhuman", + "config": { + "action_dir": "{candidate}", + "max_iterations": 60 + }, + "tools": { + "allow": [ + "file_read", + "grep", + "glob", + "list", + "read_diff", + "file_write", + "edit", + "apply_patch" + ], + "deny": [ + "wait", + "wait_loop", + "spawn_subagent", + "spawn_async_subagent", + "spawn_parallel_agents", + "web_fetch", + "shell" + ] + }, + "toolpacks": [ + "lean", + "python", + "literature", + "ledger" + ] + } + ] +} diff --git a/crates/supermath-mcp/template/hive/desks.json b/crates/supermath-mcp/template/hive/desks.json new file mode 100644 index 0000000..91003f9 --- /dev/null +++ b/crates/supermath-mcp/template/hive/desks.json @@ -0,0 +1,15 @@ +{ + "desks": [ + { + "id": "lab", + "name": "Settling lab", + "description": "settle one pinned conjecture in Lean", + "members": [ + "lead", + "prover", + "refuter" + ], + "responder_mode": "auto" + } + ] +} diff --git a/crates/supermath-mcp/template/hive/hive.json b/crates/supermath-mcp/template/hive/hive.json index 57ed872..1850205 100644 --- a/crates/supermath-mcp/template/hive/hive.json +++ b/crates/supermath-mcp/template/hive/hive.json @@ -3,140 +3,6 @@ "version": 1, "id": "{{slug}}", "name": "{{slug}}", - "roles": [ - { - "id": "lead", - "charter": "roles/lead.md" - }, - { - "id": "prover", - "charter": "roles/prover.md" - }, - { - "id": "refuter", - "charter": "roles/refuter.md" - } - ], - "seats": [ - { - "id": "lead", - "role": "lead", - "prompt": "seats/lead.md", - "template": "openhuman", - "config": { - "action_dir": "{incumbent}", - "max_iterations": 30 - }, - "tools": { - "allow": [ - "file_read", - "grep", - "glob", - "list", - "read_diff" - ], - "deny": [ - "wait", - "wait_loop", - "spawn_subagent", - "spawn_async_subagent", - "spawn_parallel_agents", - "web_fetch", - "shell" - ] - }, - "toolpacks": [ - "literature", - "ledger-read" - ] - }, - { - "id": "prover", - "role": "prover", - "prompt": "seats/prover.md", - "template": "openhuman", - "config": { - "action_dir": "{candidate}", - "max_iterations": 60 - }, - "tools": { - "allow": [ - "file_read", - "grep", - "glob", - "list", - "read_diff", - "file_write", - "edit", - "apply_patch" - ], - "deny": [ - "wait", - "wait_loop", - "spawn_subagent", - "spawn_async_subagent", - "spawn_parallel_agents", - "web_fetch", - "shell" - ] - }, - "toolpacks": [ - "lean", - "literature", - "ledger" - ] - }, - { - "id": "refuter", - "role": "refuter", - "prompt": "seats/refuter.md", - "template": "openhuman", - "config": { - "action_dir": "{candidate}", - "max_iterations": 60 - }, - "tools": { - "allow": [ - "file_read", - "grep", - "glob", - "list", - "read_diff", - "file_write", - "edit", - "apply_patch" - ], - "deny": [ - "wait", - "wait_loop", - "spawn_subagent", - "spawn_async_subagent", - "spawn_parallel_agents", - "web_fetch", - "shell" - ] - }, - "toolpacks": [ - "lean", - "python", - "literature", - "ledger" - ] - } - ], - "desks": [ - { - "id": "lab", - "name": "Settling lab", - "description": "settle one pinned conjecture in Lean", - "members": [ - "lead", - "prover", - "refuter" - ], - "responder_mode": "auto" - } - ], "context": [ "context/rules.md" ], From 44bb3fdbf8ab6c5029d11be699b4e8de5a87e51b Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sun, 25 Oct 2026 09:34:12 +0000 Subject: [PATCH 2/6] refactor(scaffold): generate tools manifest directly The scaffold no longer patches the hive.json template to inject tools; it now writes a dedicated tools.json built from scratch. This keeps the template untouched and makes the generated manifest independent of the template's contents. Auto-committed-on: cuttlefish Co-authored-by: Medulla --- crates/supermath-mcp/src/cli/scaffold/mod.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/crates/supermath-mcp/src/cli/scaffold/mod.rs b/crates/supermath-mcp/src/cli/scaffold/mod.rs index 624dcbf..6c36bb0 100644 --- a/crates/supermath-mcp/src/cli/scaffold/mod.rs +++ b/crates/supermath-mcp/src/cli/scaffold/mod.rs @@ -165,13 +165,9 @@ pub fn create( .replace("{{statement}}", statement.trim_end()) }; for (path, text) in TEMPLATE { - let text = if path == "hive/hive.json" { - hive_manifest(&render(text), program, registry)? - } else { - render(text) - }; - write(&dir.join(path), &text)?; + write(&dir.join(path), &render(text))?; } + write(&dir.join(TOOLS_PATH), &tool_manifest(program, registry)?)?; write(&dir.join(STATEMENT_PATH), statement) } @@ -183,10 +179,9 @@ fn declares_conjecture(statement: &str) -> bool { .any(|line| line.trim_start().starts_with("def Conjecture")) } -/// The template's `hive.json` with the toolkit's tools and toolpacks added. -fn hive_manifest(text: &str, program: &Program, registry: &ToolRegistry) -> Result { - let mut manifest: Map = - serde_json::from_str(text).map_err(|error| Error::Template(error.to_string()))?; +/// The hive's `tools.json`: the toolkit's tools and its domain toolpacks. +fn tool_manifest(program: &Program, registry: &ToolRegistry) -> Result { + let mut manifest = Map::new(); let tools: Vec = match declarations(registry, program) { Value::Array(tools) => tools .into_iter() From a72990e1b74a589266f10f49e1d4ac32f9e15835 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sun, 25 Oct 2026 09:34:36 +0000 Subject: [PATCH 3/6] feat(scaffold): split the hive manifest into agents, desks and tools The scaffolded hive now writes its identity, roles and seats to agents.json, its desks to desks.json, and its tools and toolpacks to tools.json instead of one large hive.json, matching the layout tinyhivemind allows. Tests were updated to read the toolkit from the new tools file and to assert that the manifest is split. Auto-committed-on: cuttlefish Co-authored-by: Medulla --- crates/supermath-mcp/src/cli/scaffold/mod.rs | 13 +++++-- crates/supermath-mcp/src/cli/scaffold/test.rs | 34 +++++++++++++++++-- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/crates/supermath-mcp/src/cli/scaffold/mod.rs b/crates/supermath-mcp/src/cli/scaffold/mod.rs index 6c36bb0..4792ee9 100644 --- a/crates/supermath-mcp/src/cli/scaffold/mod.rs +++ b/crates/supermath-mcp/src/cli/scaffold/mod.rs @@ -8,14 +8,19 @@ //! ```text //! /rsi.json the subject, hive, scorer, models, budget //! /brief.md the conjecture and the epoch's task -//! /hive/ lead, prover, refuter; the toolkit as tools +//! /hive/hive.json identity, context, policies, coordinator +//! /hive/agents.json roles and seats: lead, prover, refuter +//! /hive/desks.json the desk the seats share +//! /hive/tools.json the toolkit as tools and toolpacks //! /subject/lean/Supermath/Statement.lean the conjecture, frozen //! /subject/lean/Supermath/Prove.lean `theorem proof : Conjecture` //! /subject/lean/Supermath/Refute.lean `theorem refute : ¬ Conjecture` //! ``` //! //! The statement file must declare `Supermath.Conjecture : Prop`; it is -//! copied in verbatim. `hive.json` gets one executable tool per toolkit tool +//! copied in verbatim. The hive's manifest is split the way tinyhivemind +//! allows, so no one file grows large. `tools.json` gets one executable tool +//! per toolkit tool //! (see [`declarations`]), less the `file_*` tools, //! whose work the seats' own file tools do in their checkout, and //! `web_search`, whose name `OpenHuman`'s own search tool has; and one toolpack per @@ -105,6 +110,10 @@ const TEMPLATE: [(&str, &str); 19] = [ ), ]; +/// Where the hive's tools and toolpacks go in the loop folder: written from +/// the toolkit, not the template. +const TOOLS_PATH: &str = "hive/tools.json"; + /// Where the conjecture goes in the loop folder. pub const STATEMENT_PATH: &str = "subject/lean/Supermath/Statement.lean"; diff --git a/crates/supermath-mcp/src/cli/scaffold/test.rs b/crates/supermath-mcp/src/cli/scaffold/test.rs index 085913f..cda3c7d 100644 --- a/crates/supermath-mcp/src/cli/scaffold/test.rs +++ b/crates/supermath-mcp/src/cli/scaffold/test.rs @@ -79,10 +79,40 @@ fn the_manifest_runs_this_binary_as_its_scorer_and_freezes_the_statement() { } #[test] -fn the_hive_holds_the_toolkit_less_the_file_tools_in_domain_toolpacks() { +fn the_hive_manifest_is_split_into_agents_desks_and_tools() { let (_root, dir) = scaffold(); + let section = |file: &str| -> Vec { + let mut keys: Vec = json(&dir, file) + .as_object() + .unwrap() + .keys() + .cloned() + .collect(); + keys.sort(); + keys + }; let hive = json(&dir, "hive/hive.json"); assert_eq!(hive["id"], "demo"); + for moved in ["roles", "seats", "desks", "tools", "toolpacks"] { + assert!(hive.get(moved).is_none(), "hive.json still holds {moved}"); + } + assert_eq!(section("hive/agents.json"), ["roles", "seats"]); + assert_eq!(section("hive/desks.json"), ["desks"]); + assert_eq!(section("hive/tools.json"), ["toolpacks", "tools"]); + let seats = json(&dir, "hive/agents.json")["seats"].clone(); + let ids: Vec<&str> = seats + .as_array() + .unwrap() + .iter() + .map(|seat| seat["id"].as_str().unwrap()) + .collect(); + assert_eq!(ids, ["lead", "prover", "refuter"]); +} + +#[test] +fn the_hive_holds_the_toolkit_less_the_file_tools_in_domain_toolpacks() { + let (_root, dir) = scaffold(); + let hive = json(&dir, "hive/tools.json"); let ids: Vec<&str> = hive["tools"] .as_array() .unwrap() @@ -110,7 +140,7 @@ fn the_hive_holds_the_toolkit_less_the_file_tools_in_domain_toolpacks() { assert!(pack("ledger").contains(&"recall_memory".to_owned())); assert_eq!(pack("ledger-read"), ["ledger_claims", "recall_memory"]); // Every toolpack a seat names exists. - for seat in hive["seats"].as_array().unwrap() { + for seat in json(&dir, "hive/agents.json")["seats"].as_array().unwrap() { for name in seat["toolpacks"].as_array().unwrap() { pack(name.as_str().unwrap()); } From 364cb27bfa4aaa434d6abdcefb0215e8ea075d75 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sun, 25 Oct 2026 09:36:33 +0000 Subject: [PATCH 4/6] docs(specs): document split hive manifest layout The loops spec now describes the hive directory as a four-way manifest split into hive.json, agents.json, desks.json and tools.json, and the running-a-loop section requires an rsi-ralph that reads the split manifest, noting that older binaries fail with a missing field error. Auto-committed-on: cuttlefish Co-authored-by: Medulla --- docs/specs/loops.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/specs/loops.md b/docs/specs/loops.md index 216d281..00001b1 100644 --- a/docs/specs/loops.md +++ b/docs/specs/loops.md @@ -54,9 +54,11 @@ rsi.json dir subject; editable lean/Supermath/, python/work/, notes/, .lo decided by seeds [1, 2], maximize, target 1.0; models on Surplus (keys.openrouter = SURPLUS_API_KEY, provider "*") brief.md the statement, and rsi-ralph's {{incumbent}} {{history}} {{failures}} -hive/ seats lead (incumbent, read-only), prover and refuter (each - its own candidate); `tools` = every toolkit tool but file_* - and web_search, as +hive/ the manifest split four ways: hive.json (identity, context, + policies, coordinator), agents.json (roles and seats), + desks.json, tools.json; seats lead (incumbent, read-only), + prover and refuter (each its own candidate); tools.json + `tools` = every toolkit tool but file_* and web_search, as `supermath-mcp call --project-dir . [--env-file F]`; toolpacks lean, python, literature, ledger, ledger-read subject/lean/Supermath/{Statement,Prove,Refute}.lean, lakefile.toml, lean-toolchain @@ -100,9 +102,11 @@ run without them. ### Running a loop -A loop needs an `rsi-ralph` built from `e40c7e3` (tinyhumansai/rsi-ralph#75, -which attaches a hive's executable tools to its seats) or later; there is no -tagged release yet. +A loop needs an `rsi-ralph` that reads a split hive manifest: +tinyhumansai/rsi-ralph#82, which pins tinyhivemind `a5ed63fc` +(tinyhumansai/tinyhivemind#134), or later. An older binary refuses the +folder with `missing field seats` in `hive.json`. There is no tagged release +yet. ```sh docker compose -f compose.yaml -f compose.rsi.yaml --profile host up -d lean python cortexdb From 5df6275b33a884708cd2aef1e14a58d623c071e0 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sun, 25 Oct 2026 09:36:40 +0000 Subject: [PATCH 5/6] docs(hive): clarify manifest name in module docs The module documentation now names the manifest file that `loop new` writes (`tools.json`) alongside the `tools` array it describes, so readers can find the declaration without guessing which file holds it. Auto-committed-on: cuttlefish Co-authored-by: Medulla --- crates/supermath-mcp/src/cli/hive/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/supermath-mcp/src/cli/hive/mod.rs b/crates/supermath-mcp/src/cli/hive/mod.rs index 933fbec..70fb091 100644 --- a/crates/supermath-mcp/src/cli/hive/mod.rs +++ b/crates/supermath-mcp/src/cli/hive/mod.rs @@ -1,7 +1,7 @@ //! `supermath-mcp hive-tools`: the toolkit as tinyhivemind executable tools. //! //! An `rsi-ralph` hive package declares the programs its seats may call in -//! the `tools` array of `hive.json` (tinyhivemind's +//! the `tools` array of its manifest (`tools.json` in a loop `loop new` writes; tinyhivemind's //! `docs/specs/executable-tools.md`). This module writes one declaration per //! registered tool, straight from the tool's own name, description, schema, //! and permission, so the package cannot drift from the code: From 5d22d100c60c80adcfeaef90c674426d8b3832ad Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Sun, 25 Oct 2026 09:36:51 +0000 Subject: [PATCH 6/6] docs(hive): clarify manifest tools.json wording Reworded the module doc comment to describe the tools manifest as kept in tools.json within the loops that loop new writes, making the sentence easier to read without changing any behaviour. Auto-committed-on: cuttlefish Co-authored-by: Medulla --- crates/supermath-mcp/src/cli/hive/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/supermath-mcp/src/cli/hive/mod.rs b/crates/supermath-mcp/src/cli/hive/mod.rs index 70fb091..d5912db 100644 --- a/crates/supermath-mcp/src/cli/hive/mod.rs +++ b/crates/supermath-mcp/src/cli/hive/mod.rs @@ -1,8 +1,8 @@ //! `supermath-mcp hive-tools`: the toolkit as tinyhivemind executable tools. //! //! An `rsi-ralph` hive package declares the programs its seats may call in -//! the `tools` array of its manifest (`tools.json` in a loop `loop new` writes; tinyhivemind's -//! `docs/specs/executable-tools.md`). This module writes one declaration per +//! the `tools` array of its manifest, kept in `tools.json` in the loops +//! `loop new` writes (tinyhivemind's `docs/specs/executable-tools.md`). This module writes one declaration per //! registered tool, straight from the tool's own name, description, schema, //! and permission, so the package cannot drift from the code: //!