diff --git a/crates/supermath-mcp/src/cli/hive/mod.rs b/crates/supermath-mcp/src/cli/hive/mod.rs index 933fbec..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 `hive.json` (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: //! diff --git a/crates/supermath-mcp/src/cli/scaffold/mod.rs b/crates/supermath-mcp/src/cli/scaffold/mod.rs index 61969f9..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 @@ -32,13 +37,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"), @@ -97,6 +110,10 @@ const TEMPLATE: [(&str, &str); 17] = [ ), ]; +/// 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"; @@ -157,13 +174,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) } @@ -175,10 +188,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() 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()); } 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" ], 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