From eb7e3de78293c3148ea59f31b930bac308bc9846 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 13 Aug 2026 16:56:56 +0300 Subject: [PATCH 1/2] fix(core): refresh embedded OpenHuman dependency Co-authored-by: Medulla --- Cargo.lock | 310 +++++++++++------------- Cargo.toml | 4 + src/sdk/src/flow_engine/caps/mod.rs | 6 + src/sdk/src/ui/workflows/graph.rs | 14 +- src/sdk/src/ui/workflows/graph_tests.rs | 9 + vendor/openhuman | 2 +- 6 files changed, 175 insertions(+), 170 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index aff9960f4..1f0d97bf0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -284,29 +284,6 @@ version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" -[[package]] -name = "aws-lc-rs" -version = "1.17.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1" -dependencies = [ - "aws-lc-sys", - "zeroize", -] - -[[package]] -name = "aws-lc-sys" -version = "0.43.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c" -dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", - "pkg-config", -] - [[package]] name = "axum" version = "0.8.9" @@ -314,6 +291,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90" dependencies = [ "axum-core", + "axum-macros", "base64 0.22.1", "bytes", "form_urlencoded", @@ -362,6 +340,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "axum-macros" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aa268c23bfbbd2c4363b9cd302a4f504fb2a9dfe7e3451d66f35dd392e20aca" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "backoff" version = "0.4.0" @@ -522,8 +511,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9" dependencies = [ "find-msvc-tools", - "jobserver", - "libc", "shlex", ] @@ -615,15 +602,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "cmake" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" -dependencies = [ - "cc", -] - [[package]] name = "compact_str" version = "0.8.2" @@ -1014,7 +992,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1040,12 +1018,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - [[package]] name = "dyn-clone" version = "1.0.20" @@ -1105,7 +1077,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -1247,12 +1219,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "fs_extra" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - [[package]] name = "futures" version = "0.3.33" @@ -1432,18 +1398,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "git2" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddddbf932745a6be37109b6112d3ee09696106f848449069d3a57bba937ab82e" -dependencies = [ - "bitflags 2.13.1", - "libc", - "libgit2-sys", - "log", -] - [[package]] name = "glob" version = "0.3.4" @@ -2015,16 +1969,6 @@ dependencies = [ "jiff-tzdb", ] -[[package]] -name = "jobserver" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" -dependencies = [ - "getrandom 0.4.3", - "libc", -] - [[package]] name = "js-sys" version = "0.3.103" @@ -2063,18 +2007,6 @@ version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" -[[package]] -name = "libgit2-sys" -version = "0.18.7+1.9.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c7391e4b9f4ffab1a624223cc1d7385ff9a678f490768add717de7ea2f4d89" -dependencies = [ - "cc", - "libc", - "libz-sys", - "pkg-config", -] - [[package]] name = "libm" version = "0.2.16" @@ -2101,18 +2033,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "libz-sys" -version = "1.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85bc9657773828b90eeb625adff10eeac83cc21bbfd8e23a03eaa8a33c9e28d9" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "linux-keyutils" version = "0.2.5" @@ -2171,17 +2091,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "lzma-sys" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" -dependencies = [ - "cc", - "libc", - "pkg-config", -] - [[package]] name = "matchers" version = "0.2.0" @@ -2406,7 +2315,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -2476,7 +2385,7 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "openhuman" -version = "0.63.6" +version = "0.63.7" dependencies = [ "aes-gcm", "aho-corasick", @@ -2498,7 +2407,6 @@ dependencies = [ "fs2", "futures", "futures-util", - "git2", "glob", "hex", "hkdf", @@ -2529,14 +2437,20 @@ dependencies = [ "tempfile", "thiserror 2.0.19", "tinyagents", + "tinybus", "tinychannels", "tinycortex", + "tinycortex-api", "tinyhumans-sdk", "tinyjuice", + "tinymemory", + "tinymemory-api", + "tinymemory-core", + "tinymemory-tinycortex", "tinyplace", "tokio", "tokio-stream", - "tokio-tungstenite 0.24.0", + "tokio-tungstenite 0.29.0", "tokio-util", "toml 1.1.4+spec-1.1.0", "tracing", @@ -2548,7 +2462,6 @@ dependencies = [ "walkdir", "windows-sys 0.61.2", "x25519-dalek", - "xz2", "zeroize", "zip", ] @@ -2890,7 +2803,7 @@ dependencies = [ "once_cell", "socket2", "tracing", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3279,7 +3192,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys 0.12.1", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3288,7 +3201,6 @@ version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ - "aws-lc-rs", "log", "once_cell", "ring", @@ -3314,7 +3226,6 @@ version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ - "aws-lc-rs", "ring", "rustls-pki-types", "untrusted", @@ -3942,7 +3853,7 @@ dependencies = [ "getrandom 0.4.3", "once_cell", "rustix 1.1.4", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4042,6 +3953,28 @@ dependencies = [ "tracing", ] +[[package]] +name = "tinybus" +version = "0.1.0" +dependencies = [ + "async-trait", + "serde", + "serde_json", + "thiserror 2.0.19", + "tinybus-macros", + "tokio", + "tracing", +] + +[[package]] +name = "tinybus-macros" +version = "0.1.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.119", +] + [[package]] name = "tinychannels" version = "0.1.0" @@ -4084,8 +4017,8 @@ dependencies = [ "anyhow", "async-trait", "chrono", + "dirs 5.0.1", "futures", - "git2", "log", "parking_lot", "rand 0.10.2", @@ -4098,6 +4031,7 @@ dependencies = [ "sha2 0.10.9", "thiserror 2.0.19", "tinyagents", + "tinycortex-api", "tokio", "toml 0.8.23", "tracing", @@ -4105,15 +4039,29 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tinycortex-api" +version = "0.1.1" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "serde", + "serde_json", + "sha2 0.10.9", + "thiserror 2.0.19", + "uuid", +] + [[package]] name = "tinyflows" -version = "0.6.0" +version = "0.6.1" dependencies = [ "async-trait", "axum", "futures-timer", "futures-util", - "getrandom 0.3.4", + "getrandom 0.4.3", "jaq-core", "jaq-json", "jaq-std", @@ -4160,6 +4108,77 @@ dependencies = [ "unicode-width 0.2.0", ] +[[package]] +name = "tinymemory" +version = "1.0.1" +dependencies = [ + "anyhow", + "async-trait", + "log", + "serde", + "serde_json", + "tinymemory-api", +] + +[[package]] +name = "tinymemory-api" +version = "0.1.1" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "log", + "schemars 1.2.2", + "serde", + "serde_json", + "sha2 0.11.0", + "thiserror 2.0.19", + "uuid", +] + +[[package]] +name = "tinymemory-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "axum", + "chrono", + "dirs 5.0.1", + "futures", + "log", + "parking_lot", + "rand 0.8.7", + "regex", + "reqwest", + "rusqlite", + "serde", + "serde_json", + "sha2 0.10.9", + "thiserror 2.0.19", + "tinyagents", + "tinycortex", + "tinycortex-api", + "tinymemory", + "tinymemory-api", + "tokio", + "tracing", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tinymemory-tinycortex" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "tinycortex", + "tinymemory", + "tinymemory-api", +] + [[package]] name = "tinyplace" version = "2.0.4" @@ -4182,7 +4201,7 @@ dependencies = [ "sha2 0.10.9", "thiserror 2.0.19", "tokio", - "tokio-tungstenite 0.24.0", + "tokio-tungstenite 0.29.0", "url", "x25519-dalek", ] @@ -4286,24 +4305,6 @@ dependencies = [ "tungstenite 0.21.0", ] -[[package]] -name = "tokio-tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc5f74e248dc973e0dbb7b74c7e0d6fcc301c694ff50049504004ef4d0cdcd9" -dependencies = [ - "futures-util", - "log", - "native-tls", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-native-tls", - "tokio-rustls", - "tungstenite 0.24.0", - "webpki-roots 0.26.11", -] - [[package]] name = "tokio-tungstenite" version = "0.29.0" @@ -4312,9 +4313,11 @@ checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" dependencies = [ "futures-util", "log", + "native-tls", "rustls", "rustls-pki-types", "tokio", + "tokio-native-tls", "tokio-rustls", "tungstenite 0.29.0", "webpki-roots 0.26.11", @@ -4535,27 +4538,6 @@ dependencies = [ "utf-8", ] -[[package]] -name = "tungstenite" -version = "0.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18e5b8366ee7a95b16d32197d0b2604b43a0be89dc5fac9f8e96ccafbaedda8a" -dependencies = [ - "byteorder", - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "native-tls", - "rand 0.8.7", - "rustls", - "rustls-pki-types", - "sha1", - "thiserror 1.0.69", - "utf-8", -] - [[package]] name = "tungstenite" version = "0.29.0" @@ -4567,6 +4549,7 @@ dependencies = [ "http", "httparse", "log", + "native-tls", "rand 0.9.5", "rustls", "rustls-pki-types", @@ -4919,7 +4902,7 @@ version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -5416,15 +5399,6 @@ dependencies = [ "rustix 1.1.4", ] -[[package]] -name = "xz2" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" -dependencies = [ - "lzma-sys", -] - [[package]] name = "yoke" version = "0.8.3" diff --git a/Cargo.toml b/Cargo.toml index 2ed04fd28..337aeac01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -120,6 +120,10 @@ unicode-width = "0.2" tinyagents = { path = "vendor/openhuman/vendor/tinyagents" } tinychannels = { path = "vendor/openhuman/vendor/tinychannels" } tinycortex = { path = "vendor/openhuman/vendor/tinycortex" } +# OpenHuman's extracted TinyMemory core names this contract by version. The +# vendored OpenHuman manifest patches it, but path dependencies do not inherit +# their own patch tables, so Medulla must bind it at this workspace root too. +tinycortex-api = { path = "vendor/openhuman/vendor/tinycortex/api" } tinyflows = { path = "vendor/openhuman/vendor/tinyflows" } tinyjuice = { path = "vendor/openhuman/vendor/tinyjuice" } # Load-bearing even though no crate in this workspace links tinyplace any more: diff --git a/src/sdk/src/flow_engine/caps/mod.rs b/src/sdk/src/flow_engine/caps/mod.rs index 05310484b..3fd014325 100644 --- a/src/sdk/src/flow_engine/caps/mod.rs +++ b/src/sdk/src/flow_engine/caps/mod.rs @@ -177,6 +177,12 @@ fn build_capabilities_inner( services.http_credentials, )), code, + // TinyFlows owns the shell-node contract, but Medulla has not yet + // adapted its path, environment, and interpreter policy to that + // capability. Refuse shell nodes explicitly until that boundary exists + // rather than running an author-controlled command with the code + // runner's looser shape. + shell: None, state: Arc::new(FileStateStore::new(&settings.state_dir, state_namespace)), resolver: services.resolver, // `None` until the host exposes a memory store: the engine then fails a diff --git a/src/sdk/src/ui/workflows/graph.rs b/src/sdk/src/ui/workflows/graph.rs index 0a114e2f3..afc1f790e 100644 --- a/src/sdk/src/ui/workflows/graph.rs +++ b/src/sdk/src/ui/workflows/graph.rs @@ -442,6 +442,7 @@ pub fn kind_wire(kind: &NodeKind) -> &'static str { NodeKind::ToolCall => "tool_call", NodeKind::HttpRequest => "http_request", NodeKind::Code => "code", + NodeKind::Shell => "shell", NodeKind::Condition => "condition", NodeKind::Switch => "switch", NodeKind::Merge => "merge", @@ -467,6 +468,7 @@ pub fn kind_glyph(kind: &NodeKind) -> &'static str { NodeKind::ToolCall => "⚒", NodeKind::HttpRequest => "⇅", NodeKind::Code => "λ", + NodeKind::Shell => "$", NodeKind::Condition => "◆", NodeKind::Switch => "⑂", NodeKind::Merge => "⊕", @@ -498,7 +500,11 @@ pub fn kind_color(kind: &NodeKind) -> &'static str { NodeKind::Agent | NodeKind::SubWorkflow => "magenta", // Grouped with the reaching-outside kinds: a memory node's result comes // from the host's store, not from anything the graph carries. - NodeKind::ToolCall | NodeKind::HttpRequest | NodeKind::Code | NodeKind::Memory => "cyan", + NodeKind::ToolCall + | NodeKind::HttpRequest + | NodeKind::Code + | NodeKind::Shell + | NodeKind::Memory => "cyan", // `dedup` reads durable state, but what it *does* to the graph is route: // an item either continues or is dropped, so it reads with the control // flow rather than with the kinds that reach outside the process. @@ -554,6 +560,12 @@ pub fn node_summary(node: &Node) -> String { } } NodeKind::Code => text("language").unwrap_or_default(), + NodeKind::Shell => text("script_path") + .or_else(|| { + text("source") + .map(|source| source.split('\n').next().unwrap_or(&source).to_string()) + }) + .unwrap_or_default(), NodeKind::Condition => text("expression") .or_else(|| text("left")) .unwrap_or_default(), diff --git a/src/sdk/src/ui/workflows/graph_tests.rs b/src/sdk/src/ui/workflows/graph_tests.rs index 09089acbb..4f4018739 100644 --- a/src/sdk/src/ui/workflows/graph_tests.rs +++ b/src/sdk/src/ui/workflows/graph_tests.rs @@ -367,6 +367,7 @@ fn every_kind_has_a_wire_name_a_glyph_and_a_colour() { NodeKind::ToolCall, NodeKind::HttpRequest, NodeKind::Code, + NodeKind::Shell, NodeKind::Condition, NodeKind::Switch, NodeKind::Merge, @@ -402,6 +403,14 @@ fn a_summary_names_what_identifies_the_node_for_its_kind() { node("a", NodeKind::Agent, json!({"prompt":"first line\nsecond"})), "first line", ), + ( + node( + "s", + NodeKind::Shell, + json!({"source":"echo first\necho second"}), + ), + "echo first", + ), ( node("c", NodeKind::Condition, json!({"expression":"=.ok"})), "=.ok", diff --git a/vendor/openhuman b/vendor/openhuman index e29bfc66f..baaf89490 160000 --- a/vendor/openhuman +++ b/vendor/openhuman @@ -1 +1 @@ -Subproject commit e29bfc66fc4c64f9c803d08bfced6b91190108f7 +Subproject commit baaf89490da42a4c0ac4be6574adec08fe094e77 From b3d0058a9c49c5191257b7fb61fda0369ebecc92 Mon Sep 17 00:00:00 2001 From: Steven Enamakel Date: Thu, 13 Aug 2026 19:58:03 +0300 Subject: [PATCH 2/2] fix(ci): initialize OpenHuman path dependencies Co-authored-by: Medulla --- scripts/init-submodules.sh | 11 +++++++---- src/sdk/src/flow_engine/tests/mod.rs | 21 +++++++++++++++++++++ src/sdk/src/ui/workflows/graph_tests.rs | 13 +++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/scripts/init-submodules.sh b/scripts/init-submodules.sh index df9805aae..f145dcb46 100755 --- a/scripts/init-submodules.sh +++ b/scripts/init-submodules.sh @@ -34,15 +34,18 @@ cd "$(dirname "$0")/.." # The embedded OpenHuman core. git submodule update --init --depth 1 vendor/openhuman -# Its vendored crates, which the root patch table redirects Cargo to. This list -# must stay in lockstep with the `[patch.crates-io]` table in Cargo.toml. +# Its vendored crates and direct path dependencies. The crates patched by the +# root manifest must stay in lockstep with its `[patch.crates-io]` table; +# tinybus and tinymemory are unpublished direct paths from OpenHuman's manifest. git -C vendor/openhuman submodule update --init --depth 1 \ vendor/tinyagents \ + vendor/tinybus \ vendor/tinychannels \ vendor/tinycortex \ vendor/tinyflows \ vendor/tinyhumans-sdk \ vendor/tinyjuice \ - vendor/tinyplace + vendor/tinyplace \ + vendor/tinymemory -echo "Submodules initialized (OpenHuman core + its seven vendored crates)." +echo "Submodules initialized (OpenHuman core + its nine required dependencies)." diff --git a/src/sdk/src/flow_engine/tests/mod.rs b/src/sdk/src/flow_engine/tests/mod.rs index ff7280d9b..848dd1f36 100644 --- a/src/sdk/src/flow_engine/tests/mod.rs +++ b/src/sdk/src/flow_engine/tests/mod.rs @@ -271,6 +271,27 @@ fn a_node_may_name_its_instruction_prompt_or_instruction() { assert!(err.to_string().contains("prompt"), "got {err}"); } +#[test] +fn production_capabilities_refuse_shell_execution_until_its_policy_exists() { + let root = tempfile::tempdir().unwrap(); + let caps = build_capabilities( + settings(root.path()), + HostServices { + node_progress: None, + dispatch: RecordingDispatch::replying("unused"), + resolver: empty_resolver(root.path()), + http_credentials: HashMap::new(), + }, + "workflow:demo", + "run-shell-boundary", + ); + + assert!( + caps.shell.is_none(), + "shell nodes must remain unavailable until Medulla supplies path, environment, and interpreter policy" + ); +} + #[test] fn a_json_reply_is_surfaced_structurally_as_well_as_textually() { let value = reply_to_value("{\"files\": 3}", "builder"); diff --git a/src/sdk/src/ui/workflows/graph_tests.rs b/src/sdk/src/ui/workflows/graph_tests.rs index 4f4018739..0de2a7e54 100644 --- a/src/sdk/src/ui/workflows/graph_tests.rs +++ b/src/sdk/src/ui/workflows/graph_tests.rs @@ -386,6 +386,10 @@ fn every_kind_has_a_wire_name_a_glyph_and_a_colour() { kind_wire(&kind) ); } + + assert_eq!(kind_wire(&NodeKind::Shell), "shell"); + assert_eq!(kind_glyph(&NodeKind::Shell), "$"); + assert_eq!(kind_color(&NodeKind::Shell), "cyan"); } #[test] @@ -407,10 +411,19 @@ fn a_summary_names_what_identifies_the_node_for_its_kind() { node( "s", NodeKind::Shell, + json!({"script_path":"scripts/backup.sh","source":"echo ignored"}), + ), + "scripts/backup.sh", + ), + ( + node( + "s-source", + NodeKind::Shell, json!({"source":"echo first\necho second"}), ), "echo first", ), + (node("s-empty", NodeKind::Shell, json!({})), ""), ( node("c", NodeKind::Condition, json!({"expression":"=.ok"})), "=.ok",