From db6cbd915e5193edf9e8ba913a15948b0a2fdfbd Mon Sep 17 00:00:00 2001 From: oddessentials Date: Thu, 12 Nov 2026 18:55:27 -0500 Subject: [PATCH 1/3] Check every character Studio makes with Grove Companion's own check Grove Companion takes Studio's character pieces, and checks them with its own Rust code rather than Agent Guild's. CI now builds that code from agent-guild-companions at a pinned commit (tools/companion_check) and runs it on every character the suites made, as Add character does. The agents' page points to Companion for using a character as the desktop spirit. Co-Authored-By: Claude Opus 5.5 --- .github/workflows/ci.yml | 17 ++ .gitignore | 1 + site/index.md | 4 + tools/companion_check/.gdignore | 0 tools/companion_check/Cargo.lock | 249 ++++++++++++++++++++++ tools/companion_check/Cargo.toml | 9 + tools/companion_check/build.rs | 10 + tools/companion_check/rust-toolchain.toml | 3 + tools/companion_check/src/main.rs | 41 ++++ 9 files changed, 334 insertions(+) create mode 100644 tools/companion_check/.gdignore create mode 100644 tools/companion_check/Cargo.lock create mode 100644 tools/companion_check/Cargo.toml create mode 100644 tools/companion_check/build.rs create mode 100644 tools/companion_check/rust-toolchain.toml create mode 100644 tools/companion_check/src/main.rs diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eb56723..1271b8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -27,6 +27,7 @@ env: GODOT_WINDOWS_SHA512: 83decd58fdf67b9d657958a1ae6bf1929c20785315a81effe245874cdc57acb709bf868e00778a96984338c1b29dafdb453c6847747694621c6ecf5da2259993 GODOT_MACOS_SHA512: 38aa16e5bba2083941fc5b3e54be0089bd4cc35e32415f5b9fd9a8a6a7b9818255d44532ea8ef94b5aef56c4b407c2d634fa4f657e4ebe681ebbf59b7bac69ca AGENT_GUILD_COMMIT: 3a3cd721f713068ff78264560e29bf951ff9a37b + COMPANIONS_COMMIT: 05dfd1f5c0d64963048c67a826f0758d31b7e83d jobs: build: @@ -105,6 +106,14 @@ jobs: run: | node tools/check_with_agent_guild.mjs build/pieces .agent-guild if ($LASTEXITCODE -ne 0) { throw "Agent Guild refused a piece Studio made." } + - name: Every character the suites made passes Grove Companion's own check + working-directory: tools/companion_check + env: + COMPANION_CHARACTERS_RS: ${{ runner.temp }}/characters.rs + run: | + Invoke-WebRequest "https://raw.githubusercontent.com/oddessentials/agent-guild-companions/$env:COMPANIONS_COMMIT/src-tauri/src/characters.rs" -OutFile $env:COMPANION_CHARACTERS_RS + cargo run --locked -- ../../build/pieces + if ($LASTEXITCODE -ne 0) { throw "Grove Companion refused a character Studio made." } - name: The agents' page gives the app's own connect commands run: | & $env:GODOT --headless --path . --script res://tests/site_commands.gd @@ -269,6 +278,14 @@ jobs: run: | node tools/check_with_agent_guild.mjs build/pieces .agent-guild if ($LASTEXITCODE -ne 0) { throw "Agent Guild refused a piece Studio made." } + - name: Every character the suites made passes Grove Companion's own check + working-directory: tools/companion_check + env: + COMPANION_CHARACTERS_RS: ${{ runner.temp }}/characters.rs + run: | + Invoke-WebRequest "https://raw.githubusercontent.com/oddessentials/agent-guild-companions/$env:COMPANIONS_COMMIT/src-tauri/src/characters.rs" -OutFile $env:COMPANION_CHARACTERS_RS + cargo run --locked -- ../../build/pieces + if ($LASTEXITCODE -ne 0) { throw "Grove Companion refused a character Studio made." } - name: The agents' page gives the app's own connect commands run: | & $env:GODOT --headless --path . --script res://tests/site_commands.gd diff --git a/.gitignore b/.gitignore index 094f10d..58a42dc 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ *.tmp __pycache__/ node_modules/ +/tools/companion_check/target/ diff --git a/site/index.md b/site/index.md index beaa3c1..b06ced5 100644 --- a/site/index.md +++ b/site/index.md @@ -181,3 +181,7 @@ Follow step 4 above. ## Mac update Run Mac step 2 again with the same folder. The connection, the downloaded art and the person's pieces are kept. Then start a new session, so the Studio tools load again. + +## Use a character in Grove Companion + +A character Studio makes can also be the person's desktop spirit in Agent Guild: Grove Companion. Follow Companion's own steps at [oddessentials.github.io/agent-guild-companions](https://oddessentials.github.io/agent-guild-companions/), with the folder `list_pieces` gives for the character. Grove Companion takes characters, not companions. diff --git a/tools/companion_check/.gdignore b/tools/companion_check/.gdignore new file mode 100644 index 0000000..e69de29 diff --git a/tools/companion_check/Cargo.lock b/tools/companion_check/Cargo.lock new file mode 100644 index 0000000..f64588d --- /dev/null +++ b/tools/companion_check/Cargo.lock @@ -0,0 +1,249 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cfg-if" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7648175b45a9a48536d676f68d918270699102aa8dab5496df06904c914600" + +[[package]] +name = "companion_check" +version = "0.0.0" +dependencies = [ + "serde_json", + "uuid", +] + +[[package]] +name = "futures-core" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" + +[[package]] +name = "futures-task" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" + +[[package]] +name = "futures-util" +version = "0.3.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi", +] + +[[package]] +name = "itoa" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3bd1574e617a9a6c1f8f8181bd7e841e1f20e4277607ef28945765188d6284d4" + +[[package]] +name = "js-sys" +version = "0.3.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dc09d2154098bbf4ef54eab6b117a3c24cbbffe94046b16ee6d6d4510584d3a" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.191" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65cd87c9a6d60536580b316ad985726104730a5151c99845f2490428ef91240a" + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.107" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "serde" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.229" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7e9cc8b1b85264074fbcc02a88680c4096b1e47df8f739dceb03bf482f04bd6" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae9ccbea3a7b0b09443036490c8f34a8b1b760fd345e5c0dae0aac718faea79" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2c754d6c33795a1c324727428e5a7dedb5b06195f9890bdbcba760d3e246563" + +[[package]] +name = "uuid" +version = "1.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a0e2d11620c50695fe12bbbfde96b3477fe606e7284c22d505c12ce51214514" +dependencies = [ + "getrandom", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0181c14ddd6c7b8b0b0132a5f1ea2f32be0d893cd3ea97b6e3b28886b8a3d1c" +dependencies = [ + "cfg-if", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e931a02a39abdbf66966fc35902b5ed58344750a70885bed4ebb3d8eaf28ed9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca4b54280e455bd831e33a6b8e734baf432fe63fbc35c42ac14ce67a043a53a" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b1f9c3798725016c6ee5f2f57869ec0831e8bbc0ab0310a86e787e97f41389e" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "zmij" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" diff --git a/tools/companion_check/Cargo.toml b/tools/companion_check/Cargo.toml new file mode 100644 index 0000000..65884e8 --- /dev/null +++ b/tools/companion_check/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "companion_check" +version = "0.0.0" +edition = "2021" +publish = false + +[dependencies] +serde_json = "=1.0.154" +uuid = { version = "=1.29.0", features = ["v4"] } diff --git a/tools/companion_check/build.rs b/tools/companion_check/build.rs new file mode 100644 index 0000000..65f0859 --- /dev/null +++ b/tools/companion_check/build.rs @@ -0,0 +1,10 @@ +use std::{env, fs, path::PathBuf}; + +fn main() { + println!("cargo:rerun-if-env-changed=COMPANION_CHARACTERS_RS"); + let source = env::var("COMPANION_CHARACTERS_RS").expect("Set COMPANION_CHARACTERS_RS to Grove Companion's src-tauri/src/characters.rs."); + println!("cargo:rerun-if-changed={source}"); + let text = fs::read_to_string(&source).unwrap_or_else(|err| panic!("{source}: {err}")); + let items: String = text.lines().filter(|line| !line.starts_with("//!")).map(|line| format!("{line}\n")).collect(); + fs::write(PathBuf::from(env::var("OUT_DIR").unwrap()).join("characters.rs"), items).unwrap(); +} diff --git a/tools/companion_check/rust-toolchain.toml b/tools/companion_check/rust-toolchain.toml new file mode 100644 index 0000000..0b8307a --- /dev/null +++ b/tools/companion_check/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.99.1" +profile = "minimal" diff --git a/tools/companion_check/src/main.rs b/tools/companion_check/src/main.rs new file mode 100644 index 0000000..cf1add4 --- /dev/null +++ b/tools/companion_check/src/main.rs @@ -0,0 +1,41 @@ +use std::{env, fs, path::PathBuf, process::ExitCode}; + +#[allow(dead_code)] +mod characters { + include!(concat!(env!("OUT_DIR"), "/characters.rs")); +} + +fn kind(folder: &PathBuf) -> Option { + let manifest: serde_json::Value = serde_json::from_slice(&fs::read(folder.join("piece.json")).ok()?).ok()?; + manifest.get("kind")?.as_str().map(str::to_string) +} + +fn main() -> ExitCode { + let Some(pieces) = env::args().nth(1) else { + eprintln!("Usage: companion_check "); + return ExitCode::FAILURE; + }; + let mut folders: Vec = fs::read_dir(&pieces).expect("the pieces folder can't be read").flatten().map(|entry| entry.path()).filter(|path| path.is_dir()).collect(); + folders.sort(); + let scratch = env::temp_dir().join(format!("studio-companion-check-{}", characters::new_id())); + let (mut checked, mut refused) = (0, 0); + for folder in folders { + if kind(&folder).as_deref() != Some("character") { + continue; + } + checked += 1; + let name = folder.file_name().unwrap().to_string_lossy().into_owned(); + let token = characters::new_id(); + match characters::stage(&scratch, &folder, &token) { + Ok(title) => println!("ok {name} ({title})"), + Err(reason) => { + refused += 1; + println!("REFUSED {name}: {reason}"); + } + } + characters::discard(&scratch, &token); + } + let _ = fs::remove_dir_all(&scratch); + println!("{} of {checked} characters pass Grove Companion's check", checked - refused); + if checked == 0 || refused > 0 { ExitCode::FAILURE } else { ExitCode::SUCCESS } +} From fbf3f104dd5b369d492cd7689585c20b38208dc4 Mon Sep 17 00:00:00 2001 From: oddessentials Date: Thu, 12 Nov 2026 18:58:47 -0500 Subject: [PATCH 2/3] Name Agent Guild: Companions on the agents' page Co-Authored-By: Claude Opus 5.5 --- site/index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/site/index.md b/site/index.md index b06ced5..0827028 100644 --- a/site/index.md +++ b/site/index.md @@ -182,6 +182,6 @@ Follow step 4 above. Run Mac step 2 again with the same folder. The connection, the downloaded art and the person's pieces are kept. Then start a new session, so the Studio tools load again. -## Use a character in Grove Companion +## Use a character in Agent Guild: Companions -A character Studio makes can also be the person's desktop spirit in Agent Guild: Grove Companion. Follow Companion's own steps at [oddessentials.github.io/agent-guild-companions](https://oddessentials.github.io/agent-guild-companions/), with the folder `list_pieces` gives for the character. Grove Companion takes characters, not companions. +A character Studio makes can also be the person's desktop spirit in Agent Guild: Companions. Follow its own steps at [oddessentials.github.io/agent-guild-companions](https://oddessentials.github.io/agent-guild-companions/), with the folder `list_pieces` gives for the character. It takes characters, not companion pieces. From 9fd9e6f37d6d64b25aae01a7df590a685870f259 Mon Sep 17 00:00:00 2001 From: oddessentials Date: Thu, 12 Nov 2026 19:01:38 -0500 Subject: [PATCH 3/3] Drop a sentence from the agents' page Co-Authored-By: Claude Opus 5.5 --- site/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/index.md b/site/index.md index 0827028..a26c795 100644 --- a/site/index.md +++ b/site/index.md @@ -184,4 +184,4 @@ Run Mac step 2 again with the same folder. The connection, the downloaded art an ## Use a character in Agent Guild: Companions -A character Studio makes can also be the person's desktop spirit in Agent Guild: Companions. Follow its own steps at [oddessentials.github.io/agent-guild-companions](https://oddessentials.github.io/agent-guild-companions/), with the folder `list_pieces` gives for the character. It takes characters, not companion pieces. +A character Studio makes can also be the person's desktop spirit in Agent Guild: Companions. Follow its own steps at [oddessentials.github.io/agent-guild-companions](https://oddessentials.github.io/agent-guild-companions/), with the folder `list_pieces` gives for the character.