From 40a65f4a1e6217df2cd588fb410aca9888239eb5 Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:29:21 +0530 Subject: [PATCH 01/12] feat(build): add default-ON `skills` Cargo feature (#4798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declares the compile-time gate for the skills / skill_runtime / skill_registry domains. Default-ON so the desktop build is unchanged; slim builds opt out via --no-default-features. The dep list is intentionally empty: unlike `voice` (hound/lettre), these domains have no exclusive dependencies — every crate they touch is shared with always-on domains, and runtime_node/runtime_python are used by Agent/Flows/Memory too. The gate's value is tool-surface, prompt bloat and startup cost, not binary size. --- Cargo.toml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2b6bff7f03..fcfb6464d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -334,7 +334,7 @@ tokio = { version = "1", features = ["test-util"] } proptest = "1" [features] -default = ["tokenjuice-treesitter", "voice"] +default = ["tokenjuice-treesitter", "voice", "skills"] # AST-aware code compression (tree-sitter Rust/TS/Python grammars; C build). # On by default; disable to fall back to the brace-depth heuristic. tokenjuice-treesitter = [ @@ -351,6 +351,27 @@ tokenjuice-treesitter = [ # inference domain (shared with accessibility for cpal) and await a separate # `inference` gate. voice = ["dep:hound", "dep:lettre"] +# Skills domains: `openhuman::skills` (metadata/discovery/install), +# `openhuman::skill_runtime` (SKILL.md execution + the skill_executor agent), +# and `openhuman::skill_registry` (remote catalogs + the skill_setup agent). +# Default-ON — the desktop app always ships with skills. Slim / headless builds +# opt out via `--no-default-features --features ""`. +# Composes with the runtime `DomainSet::skills` flag (#4796): the feature +# narrows the compile-time surface, `DomainSet` gates it at runtime. +# +# The dep list is INTENTIONALLY EMPTY — do not "fix" it. Unlike `voice` +# (`hound`/`lettre`), these domains have no exclusive dependencies: every crate +# they touch (serde, serde_json, tokio, anyhow, async-trait, …) is shared with +# always-on domains, and `runtime_node`/`runtime_python` are used by Agent / +# Flows / Memory too. This gate's value is tool-surface + prompt-bloat + +# startup cost, NOT binary size. +# +# NOTE: `openhuman::skills::types` and `::ops_types` stay compiled even when +# this feature is OFF — `tools::traits` re-exports `ToolResult`/`ToolContent` +# out of `skills::types` and ~236 files consume them, so those inert serde +# types are a type carve-out, not part of the gated behaviour. See +# `src/openhuman/skills/stub.rs`. +skills = [] sandbox-landlock = ["dep:landlock"] sandbox-bubblewrap = [] peripheral-rpi = ["dep:rppal"] From f5d98c10ee789476dda72a3732399121fcc0e72a Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:29:31 +0530 Subject: [PATCH 02/12] refactor(skills): gate skills domain behind `skills` feature with type carve-out (#4798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Turns skills/mod.rs into an always-compiled facade: behavioural submodules are #[cfg(feature = "skills")], with a stub taking their place when the gate is off. skills is NOT a leaf — it is partly load-bearing infrastructure. tools/traits.rs re-exports the crate's unified ToolResult/ToolContent out of skills::types and ~236 files consume them; mcp_client and runtime_node import that path directly. So skills::types and skills::ops_types stay compiled in BOTH directions (inert serde/std-only definitions, zero coupling to gated siblings) and only behaviour is gated. Gating them wholesale would take down the entire tool trait system, MCP, and the Node runtime. The stub therefore mirrors FUNCTIONS ONLY and re-exports the real types, so there is zero type duplication — less drift surface than the voice stub, which had to re-declare SttResult + SttProvider. --- src/openhuman/skills/mod.rs | 65 ++++++++++++++++++++-- src/openhuman/skills/stub.rs | 101 +++++++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 4 deletions(-) create mode 100644 src/openhuman/skills/stub.rs diff --git a/src/openhuman/skills/mod.rs b/src/openhuman/skills/mod.rs index ac6643f138..41994801ae 100644 --- a/src/openhuman/skills/mod.rs +++ b/src/openhuman/skills/mod.rs @@ -1,28 +1,85 @@ //! Skills metadata helpers (discovery, parse, install, run). +//! +//! ## Compile-time gate (`skills` feature) +//! +//! `pub mod skills;` is ALWAYS compiled — it is a facade. The behavioural +//! submodules below are gated behind the default-ON `skills` Cargo feature; +//! when it is off, [`stub`] takes their place and exposes the same public +//! surface that always-on callers depend on (`load_workflow_metadata`, +//! `init_workflows_dir`, `registry`, `bus`, `tools`, the controller +//! aggregators) with no-op / empty bodies. Callers therefore do **not** need +//! per-call `#[cfg]`. +//! +//! ### Type carve-out (why `types` + `ops_types` are NOT gated) +//! +//! [`types`] and [`ops_types`] stay compiled in **both** directions. They are +//! inert serde/std-only type definitions with zero coupling to the gated +//! siblings, and they are load-bearing far outside this domain: +//! `tools::traits` re-exports `ToolResult`/`ToolContent` out of [`types`] as +//! the crate's unified tool-result type (`mcp_client`, `runtime_node`, and +//! ~236 files consume it), and `Workflow`/`WorkflowFrontmatter`/ +//! `WorkflowScope` from [`ops_types`] appear in always-on agent-harness and +//! prompt signatures. Gating them would take down the entire tool trait +//! system, MCP, and the Node runtime. +//! +//! The generalizable rule: **put inert types in a dep-free submodule and leave +//! it ungated; stub only the behaviour.** The stub below therefore mirrors +//! FUNCTIONS ONLY and re-exports the real types — zero type duplication, so +//! strictly less drift surface than the `voice` stub (which had to re-declare +//! `SttResult` + the `SttProvider` trait because those live inside its gated +//! tree). +//! +//! Signatures in [`stub`] must match the real ones exactly — the disabled +//! build (`cargo check --no-default-features --features tokenjuice-treesitter`) +//! is the only thing that catches drift. +// Type carve-out: always compiled, both feature directions. See module docs. +pub mod ops_types; +pub mod types; + +#[cfg(feature = "skills")] pub mod bus; +#[cfg(feature = "skills")] pub mod ops; +#[cfg(feature = "skills")] pub mod ops_create; +#[cfg(feature = "skills")] pub mod ops_discover; +#[cfg(feature = "skills")] pub mod ops_install; +#[cfg(feature = "skills")] pub mod ops_parse; -pub mod ops_types; +#[cfg(feature = "skills")] pub mod preflight; +#[cfg(feature = "skills")] pub mod registry; +#[cfg(feature = "skills")] pub mod run_log; +#[cfg(feature = "skills")] pub mod schemas; +#[cfg(feature = "skills")] pub mod tools; -pub mod types; -#[cfg(test)] +#[cfg(all(test, feature = "skills"))] #[path = "e2e_plumbing_tests.rs"] mod e2e_plumbing_tests; -#[cfg(test)] +#[cfg(all(test, feature = "skills"))] #[path = "e2e_run_tests.rs"] mod e2e_run_tests; +#[cfg(feature = "skills")] pub use ops::*; +#[cfg(feature = "skills")] pub use schemas::{ all_skills_controller_schemas, all_skills_registered_controllers, skills_schemas, }; + +// --------------------------------------------------------------------------- +// Disabled facade — compiled only when the `skills` feature is OFF. +// --------------------------------------------------------------------------- + +#[cfg(not(feature = "skills"))] +mod stub; +#[cfg(not(feature = "skills"))] +pub use stub::*; diff --git a/src/openhuman/skills/stub.rs b/src/openhuman/skills/stub.rs new file mode 100644 index 0000000000..a250923198 --- /dev/null +++ b/src/openhuman/skills/stub.rs @@ -0,0 +1,101 @@ +//! Disabled-skills facade. +//! +//! Compiled only when the `skills` Cargo feature is OFF (see the gate in +//! [`super`]). It mirrors the subset of the real `skills` public surface that +//! always-on callers depend on, with no-op / empty bodies so the crate still +//! compiles, boots, and serves `/rpc` without the skills domains. +//! +//! Unlike the `voice` stub, this one mirrors **functions only**: the domain's +//! types live in the ungated [`super::types`] / [`super::ops_types`] carve-out +//! and are re-exported verbatim below, so there is zero type duplication and +//! correspondingly less drift surface. +//! +//! The signatures here MUST match the real ones exactly (return types +//! included). The disabled build +//! (`cargo check --no-default-features --features tokenjuice-treesitter`) is +//! the only thing that catches drift — if a real signature changes, update the +//! mirror below until that build is green again. + +use std::path::Path; + +use crate::core::all::RegisteredController; +use crate::core::ControllerSchema; + +// --------------------------------------------------------------------------- +// Type surface — re-exported from the ungated carve-out, NOT re-declared. +// Mirrors the `pub use ops::*` → `pub use super::ops_types::{…}` chain that the +// real build exposes at the `skills` root (`skills::Workflow`, etc.). +// --------------------------------------------------------------------------- + +pub use super::ops_types::{ + Workflow, WorkflowFrontmatter, WorkflowScope, MAX_WORKFLOW_RESOURCE_BYTES, +}; + +// --------------------------------------------------------------------------- +// Discovery surface (mirrors `ops_discover::*` re-exported at the skills root) +// --------------------------------------------------------------------------- + +/// Always empty: no skills are discovered when the domain is compiled out. +/// Callers (agent harness, channel startup, prompt rendering) treat an empty +/// catalog as "user has no skills installed", which is the correct degraded +/// behaviour. +pub fn load_workflow_metadata(_workspace_dir: &Path) -> Vec { + log::debug!("[skills-stub] load_workflow_metadata -> [] (skills disabled)"); + Vec::new() +} + +/// No-op success: with skills compiled out there is no skills directory to +/// provision, and workspace bootstrap must not fail because of it. +pub fn init_workflows_dir(_workspace_dir: &Path) -> Result<(), String> { + log::debug!("[skills-stub] init_workflows_dir skipped (skills disabled)"); + Ok(()) +} + +// --------------------------------------------------------------------------- +// Controller aggregators — empty so `src/core/all.rs` needs no `#[cfg]`. +// --------------------------------------------------------------------------- + +/// Always empty: the `openhuman.skills_*` controllers are compiled out, so +/// they never enter the registry (unknown-method over `/rpc`, absent from +/// `/schema`). +pub fn all_skills_registered_controllers() -> Vec { + Vec::new() +} + +/// Always empty — see [`all_skills_registered_controllers`]. +pub fn all_skills_controller_schemas() -> Vec { + Vec::new() +} + +// --------------------------------------------------------------------------- +// registry::prune_legacy_default_workflows +// --------------------------------------------------------------------------- + +pub mod registry { + use std::path::Path; + + /// No-op: the legacy bundled-skill prune only removes directories this + /// domain created, and it never ran when skills are compiled out. + pub fn prune_legacy_default_workflows(_workspace_dir: &Path) { + log::debug!("[skills-stub] prune_legacy_default_workflows skipped (skills disabled)"); + } +} + +// --------------------------------------------------------------------------- +// bus::{ensure_triggered_workflow_subscriber, register_workflow_cleanup_subscriber} +// --------------------------------------------------------------------------- + +pub mod bus { + /// No-op: there are no triggered skills to subscribe to the event bus for. + pub fn ensure_triggered_workflow_subscriber(_workspace: &std::path::Path) { + log::debug!("[skills-stub] ensure_triggered_workflow_subscriber skipped (skills disabled)"); + } + + /// No-op: no skill run directories exist to clean up. + pub fn register_workflow_cleanup_subscriber() {} +} + +// NOTE: no `tools` module here. The `pub use skills::tools::*` glob in +// `tools/mod.rs` is `#[cfg(feature = "skills")]` instead — mirroring the +// `voice` gate's handling of the `audio_toolkit::tools::*` glob. An empty stub +// module would re-export nothing and trip `unused_imports` at the glob site. From 3f534498fc088065862f7e28017df165484de1c5 Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:29:39 +0530 Subject: [PATCH 03/12] refactor(skill_runtime): gate skill_runtime domain behind `skills` feature (#4798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same facade+stub shape as the skills domain — the three skill domains ship as one unit under one gate. The stub is deliberately tiny: every caller of the run machinery (spawn_workflow_run_background, await_run_outcome, WorkflowRunStarted) lives inside code gated by the same feature — the run_workflow agent tool, the openhuman.skills_* handlers, and this domain's own tests — so they vanish together. It owes only what always-on code reaches: the controller aggregators (src/core/all.rs). --- src/openhuman/skill_runtime/mod.rs | 25 ++++++++++++++++++++++ src/openhuman/skill_runtime/stub.rs | 33 +++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 src/openhuman/skill_runtime/stub.rs diff --git a/src/openhuman/skill_runtime/mod.rs b/src/openhuman/skill_runtime/mod.rs index ea903d99d6..4d0dfedb5a 100644 --- a/src/openhuman/skill_runtime/mod.rs +++ b/src/openhuman/skill_runtime/mod.rs @@ -5,15 +5,40 @@ //! owns remote catalogs and install sources. This module owns actually running //! a skill, regardless of whether the skill's instructions call Python, Node, //! shell tools, or another OpenHuman agent tool. +//! +//! ## Compile-time gate (`skills` feature) +//! +//! `pub mod skill_runtime;` is ALWAYS compiled — it is a facade. The real +//! implementation is gated behind the default-ON `skills` Cargo feature (the +//! same gate as `openhuman::skills` and `openhuman::skill_registry` — the +//! three domains ship as one unit). When the feature is off, [`stub`] takes +//! its place with disabled-error / empty bodies. See +//! `src/openhuman/skills/mod.rs` for the pattern and the type carve-out. +#[cfg(feature = "skills")] pub mod agent; +#[cfg(feature = "skills")] pub mod ops; +#[cfg(feature = "skills")] mod run_machinery; +#[cfg(feature = "skills")] pub mod schemas; +#[cfg(feature = "skills")] pub mod tools; +#[cfg(feature = "skills")] pub use run_machinery::{await_run_outcome, spawn_workflow_run_background, WorkflowRunStarted}; +#[cfg(feature = "skills")] pub use schemas::{ all_skill_runtime_controller_schemas, all_skill_runtime_registered_controllers, skill_runtime_schemas, }; + +// --------------------------------------------------------------------------- +// Disabled facade — compiled only when the `skills` feature is OFF. +// --------------------------------------------------------------------------- + +#[cfg(not(feature = "skills"))] +mod stub; +#[cfg(not(feature = "skills"))] +pub use stub::*; diff --git a/src/openhuman/skill_runtime/stub.rs b/src/openhuman/skill_runtime/stub.rs new file mode 100644 index 0000000000..aa1d1aa56b --- /dev/null +++ b/src/openhuman/skill_runtime/stub.rs @@ -0,0 +1,33 @@ +//! Disabled-skills facade for the skill-runtime domain. +//! +//! Compiled only when the `skills` Cargo feature is OFF (see the gate in +//! [`super`]). Deliberately tiny: every caller of the run machinery +//! (`spawn_workflow_run_background`, `await_run_outcome`, `WorkflowRunStarted`) +//! lives inside code gated by the *same* feature — the `run_workflow` agent +//! tool, the `openhuman.skills_*` handlers, and this domain's own tests — so +//! those vanish together and the stub owes only what always-on code reaches: +//! the controller aggregators (`src/core/all.rs`) and the `tools` module glob +//! (`src/openhuman/tools/mod.rs`). +//! +//! The signatures here MUST match the real ones exactly. The disabled build +//! (`cargo check --no-default-features --features tokenjuice-treesitter`) is +//! the only thing that catches drift. + +use crate::core::all::RegisteredController; +use crate::core::ControllerSchema; + +/// Always empty: the `openhuman.skill_runtime_*` controllers are compiled out, +/// so they never enter the registry (unknown-method over `/rpc`, absent from +/// `/schema`). +pub fn all_skill_runtime_registered_controllers() -> Vec { + Vec::new() +} + +/// Always empty — see [`all_skill_runtime_registered_controllers`]. +pub fn all_skill_runtime_controller_schemas() -> Vec { + Vec::new() +} + +// NOTE: no `tools` module here — the `pub use skill_runtime::tools::*` glob in +// `tools/mod.rs` is `#[cfg(feature = "skills")]` instead, mirroring the `voice` +// gate. See the note in `skills/stub.rs`. From 5d3e365a0701c2ec101cbc8d86e866954be12371 Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:29:39 +0530 Subject: [PATCH 04/12] refactor(skill_registry): gate skill_registry domain behind `skills` feature (#4798) Same facade+stub shape. The stub mirrors only what always-on code reaches: the boot catalog refresh kicked off unconditionally by core::runtime::services, plus the controller aggregators. The catalog store, wire types and skill_setup agent are only referenced from code gated by the same feature. --- src/openhuman/skill_registry/mod.rs | 27 +++++++++++++++- src/openhuman/skill_registry/stub.rs | 46 ++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 src/openhuman/skill_registry/stub.rs diff --git a/src/openhuman/skill_registry/mod.rs b/src/openhuman/skill_registry/mod.rs index 5cacac9850..6461dd6db9 100644 --- a/src/openhuman/skill_registry/mod.rs +++ b/src/openhuman/skill_registry/mod.rs @@ -1,19 +1,44 @@ //! Skill registry: browse, search, and install skills from the aggregated //! Hermes catalog (HermesHub, ClawHub, skills.sh, LobeHub, browse.sh) //! with local caching. +//! +//! ## Compile-time gate (`skills` feature) +//! +//! `pub mod skill_registry;` is ALWAYS compiled — it is a facade. The real +//! implementation is gated behind the default-ON `skills` Cargo feature (the +//! same gate as `openhuman::skills` and `openhuman::skill_runtime` — the three +//! domains ship as one unit). When the feature is off, [`stub`] takes its +//! place with no-op / empty bodies. See `src/openhuman/skills/mod.rs` for the +//! pattern and the type carve-out. +#[cfg(feature = "skills")] pub mod agent; +#[cfg(feature = "skills")] pub mod ops; +#[cfg(feature = "skills")] pub mod schemas; +#[cfg(feature = "skills")] pub mod store; +#[cfg(feature = "skills")] pub mod tools; +#[cfg(feature = "skills")] pub mod types; +#[cfg(feature = "skills")] pub use schemas::{ all_skill_registry_controller_schemas, all_skill_registry_registered_controllers, }; /// Serializes tests that mutate the process-global `OPENHUMAN_SKILL_REGISTRY_CACHE_DIR` /// env var, so cargo's parallel runner can't interleave their cache dirs. -#[cfg(test)] +#[cfg(all(test, feature = "skills"))] pub(crate) static TEST_ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(()); + +// --------------------------------------------------------------------------- +// Disabled facade — compiled only when the `skills` feature is OFF. +// --------------------------------------------------------------------------- + +#[cfg(not(feature = "skills"))] +mod stub; +#[cfg(not(feature = "skills"))] +pub use stub::*; diff --git a/src/openhuman/skill_registry/stub.rs b/src/openhuman/skill_registry/stub.rs new file mode 100644 index 0000000000..44d72065eb --- /dev/null +++ b/src/openhuman/skill_registry/stub.rs @@ -0,0 +1,46 @@ +//! Disabled-skills facade for the skill-registry domain. +//! +//! Compiled only when the `skills` Cargo feature is OFF (see the gate in +//! [`super`]). Mirrors only what always-on code reaches: the boot catalog +//! refresh kicked off by `core::runtime::services`, the controller aggregators +//! (`src/core/all.rs`), and the `tools` module glob +//! (`src/openhuman/tools/mod.rs`). Everything else — the catalog store, wire +//! types, and the `skill_setup` agent — is only referenced from code gated by +//! the same feature, so it vanishes alongside. +//! +//! The signatures here MUST match the real ones exactly. The disabled build +//! (`cargo check --no-default-features --features tokenjuice-treesitter`) is +//! the only thing that catches drift. + +use crate::core::all::RegisteredController; +use crate::core::ControllerSchema; + +/// Always empty: the `openhuman.skill_registry_*` controllers are compiled +/// out, so they never enter the registry (unknown-method over `/rpc`, absent +/// from `/schema`). +pub fn all_skill_registry_registered_controllers() -> Vec { + Vec::new() +} + +/// Always empty — see [`all_skill_registry_registered_controllers`]. +pub fn all_skill_registry_controller_schemas() -> Vec { + Vec::new() +} + +// --------------------------------------------------------------------------- +// ops::start_boot_catalog_refresh — called unconditionally at core boot from +// `src/core/runtime/services.rs`, so it must stay callable without a `#[cfg]` +// at that always-on site. +// --------------------------------------------------------------------------- + +pub mod ops { + /// No-op: there is no remote skill catalog to warm when the skill domains + /// are compiled out. Skips the boot-time network fetch entirely. + pub fn start_boot_catalog_refresh() { + log::debug!("[skill-registry-stub] start_boot_catalog_refresh skipped (skills disabled)"); + } +} + +// NOTE: no `tools` module here — the `pub use skill_registry::tools::*` glob in +// `tools/mod.rs` is `#[cfg(feature = "skills")]` instead, mirroring the `voice` +// gate. See the note in `skills/stub.rs`. From aba6f79eac80f876ff62331125fc4021a53af3d8 Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:29:48 +0530 Subject: [PATCH 05/12] feat(tools): omit the 16 skill agent tools when `skills` is off (#4798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Concrete tool types in a Vec can't be stubbed away, so the registrations are #[cfg]-gated: RunWorkflowTool/AwaitWorkflowTool, the Workflow* metadata tools, the SkillRegistry* tools, and SkillRuntimeResolveRuntimes. Per the DoD the tool list OMITS gated tools rather than degrading them to a disabled-error. agent/tools.rs gates `run_workflow` — it sits in the always-on agent domain but is a pure skill_runtime client. The three tools/mod.rs re-export globs are #[cfg]'d rather than backed by empty stub `tools` modules, mirroring how the voice gate handles audio_toolkit::tools::* — an empty stub module re-exports nothing and trips unused_imports at the glob site. skill_allowlist feeds only the gated registrations, so it is explicitly discarded when the feature is off. --- src/openhuman/agent/tools.rs | 5 +++++ src/openhuman/tools/mod.rs | 3 +++ src/openhuman/tools/ops.rs | 21 +++++++++++++++++++++ 3 files changed, 29 insertions(+) diff --git a/src/openhuman/agent/tools.rs b/src/openhuman/agent/tools.rs index dcba4c3d27..cc399a060e 100644 --- a/src/openhuman/agent/tools.rs +++ b/src/openhuman/agent/tools.rs @@ -3,6 +3,10 @@ mod delegate; mod delegate_to_personality; mod plan_exit; pub mod remember_preference; +// Pure `skill_runtime` client (spawn + await a workflow run) — compiled out +// with the `skills` gate so the tool list OMITS these rather than degrading +// them to a disabled-error. +#[cfg(feature = "skills")] mod run_workflow; pub mod save_preference; mod todo; @@ -13,6 +17,7 @@ pub use delegate::DelegateTool; pub use delegate_to_personality::DelegateToPersonalityTool; pub use plan_exit::{PlanExitTool, PLAN_EXIT_MARKER}; pub use remember_preference::RememberPreferenceTool; +#[cfg(feature = "skills")] pub use run_workflow::{ AwaitWorkflowTool, RunWorkflowTool, AWAIT_WORKFLOW_TOOL_NAME, RUN_WORKFLOW_TOOL_NAME, }; diff --git a/src/openhuman/tools/mod.rs b/src/openhuman/tools/mod.rs index 58aef6e4a6..5709873cb7 100644 --- a/src/openhuman/tools/mod.rs +++ b/src/openhuman/tools/mod.rs @@ -46,8 +46,11 @@ pub use crate::openhuman::screen_intelligence::tools::*; pub use crate::openhuman::search::tools::*; pub use crate::openhuman::security::tools::*; pub use crate::openhuman::service::tools::*; +#[cfg(feature = "skills")] pub use crate::openhuman::skill_registry::tools::*; +#[cfg(feature = "skills")] pub use crate::openhuman::skill_runtime::tools::*; +#[cfg(feature = "skills")] pub use crate::openhuman::skills::tools::*; pub use crate::openhuman::task_sources::tools::*; pub use crate::openhuman::team::tools::*; diff --git a/src/openhuman/tools/ops.rs b/src/openhuman/tools/ops.rs index 95fb2736bb..c09ca26bd2 100644 --- a/src/openhuman/tools/ops.rs +++ b/src/openhuman/tools/ops.rs @@ -101,6 +101,11 @@ pub fn all_tools_with_runtime( skill_allowlist: Option<&std::collections::HashSet>, mcp_allowlist: Option<&[String]>, ) -> Vec> { + // `skill_allowlist` scopes only the `skills`-gated tool registrations + // below, so it is genuinely unread when that feature is compiled out. + #[cfg(not(feature = "skills"))] + let _ = skill_allowlist; + // Build a session-scoped managed Node.js bootstrap once, so ShellTool, // NodeExecTool, and NpmExecTool all share the same memoised resolution // state. Disabled when `node.enabled = false` — in that case shell skips @@ -220,7 +225,9 @@ pub fn all_tools_with_runtime( // Both wrap `skill_runtime::spawn_workflow_run_background` + // `await_run_outcome` — the same spawn path `openhuman.skills_run` // JSON-RPC uses, so RPC and tool callers stay in sync. + #[cfg(feature = "skills")] Box::new(RunWorkflowTool::new().with_skill_allowlist(skill_allowlist.cloned())), + #[cfg(feature = "skills")] Box::new(AwaitWorkflowTool::new()), Box::new(CurrentTimeTool::new()), // Reversibility for native tool-output compaction (Stage 1a): when a @@ -464,9 +471,11 @@ pub fn all_tools_with_runtime( // above, so it is not duplicated. Reads ship default-ON; the // create/install/uninstall mutators ship default-OFF via // `tools::user_filter` (install also fetches remote content). + #[cfg(feature = "skills")] Box::new( WorkflowListTool::new(config.clone()).with_skill_allowlist(skill_allowlist.cloned()), ), + #[cfg(feature = "skills")] Box::new( WorkflowDescribeTool::new(config.clone()) .with_skill_allowlist(skill_allowlist.cloned()), @@ -474,22 +483,34 @@ pub fn all_tools_with_runtime( // Skill registry tools — browse/search/install from remote registries. // Browse and search are read-only (default-ON); install is a write // operation (fetches remote content and writes to disk). + #[cfg(feature = "skills")] Box::new(SkillRegistryBrowseTool), + #[cfg(feature = "skills")] Box::new(SkillRegistrySearchTool), + #[cfg(feature = "skills")] Box::new(SkillRegistryInstallTool::new(config.clone())), + #[cfg(feature = "skills")] Box::new(SkillRegistrySourcesTool), + #[cfg(feature = "skills")] Box::new(SkillRegistryUninstallTool), // Skill runtime probes — resolve the reusable Node/Python runtimes // that skill execution relies on before a script-backed skill runs. + #[cfg(feature = "skills")] Box::new(SkillRuntimeResolveRuntimesTool::new(config.clone())), + #[cfg(feature = "skills")] Box::new( WorkflowReadResourceTool::new(config.clone()) .with_skill_allowlist(skill_allowlist.cloned()), ), + #[cfg(feature = "skills")] Box::new(WorkflowRecentRunsTool::new(config.clone())), + #[cfg(feature = "skills")] Box::new(WorkflowReadRunLogTool::new(config.clone())), + #[cfg(feature = "skills")] Box::new(WorkflowCreateTool::new(config.clone())), + #[cfg(feature = "skills")] Box::new(WorkflowInstallFromUrlTool::new(config.clone())), + #[cfg(feature = "skills")] Box::new(WorkflowUninstallTool), // Threads (conversation) tools. Read/bounded-write ship default-ON; // the destructive thread_delete / thread_purge_all ship default-OFF From 7aa9549659779f207cac0bf55506ea54253befa9 Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:29:57 +0530 Subject: [PATCH 06/12] feat(agent): drop skill builtin agents + workflow dispatch when `skills` is off (#4798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two sites the stub facade deliberately does not reach: loader.rs — the skill_setup/skill_executor BuiltinAgent entries must be #[cfg], not stubbed: include_str! embeds the agent TOML from disk regardless of module gating, so the entry itself has to disappear. task_dispatcher/executor.rs — the workflow-resolution branch. registry::get_workflow returns Option, which flattens in AgentDefinition and is destructured at the call site; stubbing it would mean re-declaring that struct, which is exactly the type duplication the carve-out exists to avoid. With the domain compiled out no handle can ever resolve to a skill, so falling through to the builtin-agent branch is the correct behaviour, not a degradation. --- src/openhuman/agent/task_dispatcher/executor.rs | 14 +++++++++++++- src/openhuman/agent_registry/agents/loader.rs | 5 +++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/src/openhuman/agent/task_dispatcher/executor.rs b/src/openhuman/agent/task_dispatcher/executor.rs index daabdc7d7a..d54571bfca 100644 --- a/src/openhuman/agent/task_dispatcher/executor.rs +++ b/src/openhuman/agent/task_dispatcher/executor.rs @@ -6,7 +6,10 @@ use std::path::Path; -use crate::openhuman::agent::harness::definition::{AgentDefinitionRegistry, PromptSource}; +use crate::openhuman::agent::harness::definition::AgentDefinitionRegistry; +// Only read by the `skills`-gated workflow-resolution branch below. +#[cfg(feature = "skills")] +use crate::openhuman::agent::harness::definition::PromptSource; use crate::openhuman::agent::harness::session::Agent; use crate::openhuman::agent::harness::subagent_runner::with_autonomous_iter_cap; use crate::openhuman::agent::task_board::TaskCardStatus; @@ -69,6 +72,15 @@ pub(super) fn resolve_executor(workspace_dir: &Path, assigned: Option<&str>) -> } // 2) Workflow (#2824): the same autonomous run, seeded with SKILL.md. + // + // `#[cfg]` rather than a stubbed `get_workflow`: the real one returns + // `Option`, which flattens in `AgentDefinition` and is + // destructured just below — stubbing it would mean re-declaring that + // struct in the disabled facade (exactly the type duplication the skills + // carve-out exists to avoid). With the domain compiled out no handle can + // ever resolve to a skill, so falling through to (3) is the correct + // behaviour, not a degradation. + #[cfg(feature = "skills")] if let Some(skill) = crate::openhuman::skills::registry::get_workflow(workspace_dir, handle) { let guidelines = match &skill.definition.system_prompt { PromptSource::Inline(s) => truncate_chars(s, EXECUTOR_PREAMBLE_MAX_CHARS), diff --git a/src/openhuman/agent_registry/agents/loader.rs b/src/openhuman/agent_registry/agents/loader.rs index bcaa34a0cf..b07d442fb0 100644 --- a/src/openhuman/agent_registry/agents/loader.rs +++ b/src/openhuman/agent_registry/agents/loader.rs @@ -263,12 +263,17 @@ pub const BUILTINS: &[BuiltinAgent] = &[ prompt_fn: super::mcp_agent::prompt::build, graph_fn: None, }, + // Skill agents — `#[cfg]` rather than stub: `include_str!` embeds the + // agent TOML from disk regardless of module gating, so the entry itself + // must disappear when the `skills` feature is off. + #[cfg(feature = "skills")] BuiltinAgent { id: "skill_setup", toml: include_str!("../../skill_registry/agent/skill_setup/agent.toml"), prompt_fn: crate::openhuman::skill_registry::agent::skill_setup::prompt::build, graph_fn: None, }, + #[cfg(feature = "skills")] BuiltinAgent { id: "skill_executor", toml: include_str!("../../skill_runtime/agent/skill_executor/agent.toml"), From 00830e93c6465001799b5bddbd5852f8ff2e4068 Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:30:06 +0530 Subject: [PATCH 07/12] test(core): both-direction coverage for the `skills` gate (#4798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the voice pair: with the feature on all three skill namespaces must be registered; with it off none may reach the registry (the stub-facade correctness gate). Also cfg's the pre-existing group_mapping_smoke assert for `skills` — group_for_namespace is registry-derived, so a compile-time-gated domain has no controller to map. CI cannot catch this class of break: the smoke lane runs cargo check, which never compiles test code. --- src/core/all_tests.rs | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/src/core/all_tests.rs b/src/core/all_tests.rs index 08412d4d26..4f2ecd6996 100644 --- a/src/core/all_tests.rs +++ b/src/core/all_tests.rs @@ -234,6 +234,42 @@ fn voice_and_audio_controllers_absent_when_feature_off() { ); } +/// With the `skills` feature on (the default), all three skill domains are +/// compiled in and registered — the desktop build is byte-identical. +#[test] +#[cfg(feature = "skills")] +fn skill_controllers_registered_when_feature_on() { + let schemas = all_controller_schemas(); + for ns in ["skills", "skill_runtime", "skill_registry"] { + assert!( + schemas.iter().any(|s| s.namespace == ns), + "`{ns}` controllers must be registered when the `skills` feature is on" + ); + } +} + +/// With the `skills` feature off, all three domains are compiled out: their +/// controllers never enter the registry, so skills RPC methods are +/// unknown-method and absent from `/schema`. This is the compile-time +/// stub-facade correctness gate (see `openhuman::skills::stub`). +/// +/// Note this does NOT cover `skills::types` / `skills::ops_types`: those stay +/// compiled in both directions (the type carve-out — `tools::traits` re-exports +/// `ToolResult`/`ToolContent` out of them), but they expose no controllers, so +/// the namespaces are absent either way. +#[test] +#[cfg(not(feature = "skills"))] +fn skill_controllers_absent_when_feature_off() { + let schemas = all_controller_schemas(); + assert!( + !schemas.iter().any(|s| s.namespace == "skills" + || s.namespace == "skill_runtime" + || s.namespace == "skill_registry"), + "skills/skill_runtime/skill_registry controllers must be compiled out \ + when the `skills` feature is off" + ); +} + #[test] fn schema_for_rpc_method_finds_known_method() { let schema = schema_for_rpc_method("openhuman.health_snapshot"); @@ -859,6 +895,9 @@ fn group_mapping_smoke() { assert_eq!(group_for_namespace("agent"), Some(DomainGroup::Agent)); // …and a representative gated one maps to its gate group. assert_eq!(group_for_namespace("flows"), Some(DomainGroup::Flows)); + // `group_for_namespace` is registry-derived, so a compile-time-gated domain + // has no controller to map. Skip when its Cargo feature is off. + #[cfg(feature = "skills")] assert_eq!(group_for_namespace("skills"), Some(DomainGroup::Skills)); assert_eq!(group_for_namespace("voice"), Some(DomainGroup::Voice)); assert_eq!(group_for_namespace("wallet"), Some(DomainGroup::Web3)); From a2007868892a65036cb50153491ea0369a724673 Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:30:06 +0530 Subject: [PATCH 08/12] build(tauri): enable the `skills` feature on the shipped app (#4798) openhuman_core is depended on with default-features = false, so the new default-ON gate must be named explicitly or the desktop app would ship without the skill domains. Multi-line array so sibling gates append cleanly. --- app/src-tauri/Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src-tauri/Cargo.toml b/app/src-tauri/Cargo.toml index 177210c7e1..36fc54e84c 100644 --- a/app/src-tauri/Cargo.toml +++ b/app/src-tauri/Cargo.toml @@ -132,7 +132,9 @@ cef = { version = "=146.4.1", default-features = false } # by tying the core's lifetime to the GUI process. The existing port-7788 # probe in `core_process::ensure_running` still attaches to a running # `openhuman-core run` harness when one is already listening. -openhuman_core = { path = "../..", package = "openhuman", default-features = false } +openhuman_core = { path = "../..", package = "openhuman", default-features = false, features = [ + "skills", +] } tinyjuice = { version = "0.2.1", default-features = false } [target.'cfg(unix)'.dependencies] From ce617363ee12d9c21e41ad35316e705fafd6f487 Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:30:16 +0530 Subject: [PATCH 09/12] docs(agents): document the `skills` gate + the type carve-out pattern (#4798) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds the gate-table row and a per-gate note covering the carve-out — the generalizable rule for the remaining gates: put a domain's inert types in a dep-free submodule and leave it ungated; stub only the behaviour. Also records why the two #[cfg] call sites can't be stubbed, and why the dep list is intentionally empty. ci-lite: no functional change. The smoke lane is an allow-list (--no-default-features --features tokenjuice-treesitter), so every new default-ON gate is exercised in its disabled direction automatically. Renames the step off "voice gate" since it now covers all of them. --- .github/workflows/ci-lite.yml | 6 +++++- AGENTS.md | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-lite.yml b/.github/workflows/ci-lite.yml index 870276ae6d..8572f9f385 100644 --- a/.github/workflows/ci-lite.yml +++ b/.github/workflows/ci-lite.yml @@ -382,7 +382,11 @@ jobs: cache-on-failure: true shared-key: pr-rust-feature-gate-smoke - - name: Check core builds with the voice gate disabled + # Allow-list, not a deny-list: only `tokenjuice-treesitter` is enabled, so + # EVERY default-ON domain gate (voice, skills, …) is exercised in its + # disabled direction here. New gates are covered automatically — no new + # lane needed. This is the only thing that catches stub/facade drift. + - name: Check core builds with every domain gate disabled run: bash scripts/ci-cancel-aware.sh cargo check --manifest-path Cargo.toml --no-default-features --features tokenjuice-treesitter rust-core-coverage: diff --git a/AGENTS.md b/AGENTS.md index 03ecc7e77c..8dc452d3c4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -233,11 +233,27 @@ GGML_NATIVE=OFF cargo check --manifest-path Cargo.toml \ | Feature | Default | Gates | Drops deps | | ------- | ------- | ----- | ---------- | | `voice` | ON | `openhuman::voice` + `openhuman::audio_toolkit` domains — STT/TTS providers, dictation server, always-on listening, podcast audio + email | `hound`, `lettre` | +| `skills` | ON | `openhuman::skills` + `openhuman::skill_runtime` + `openhuman::skill_registry` domains — SKILL.md discovery/parse/install, workflow execution + run logs, remote catalogs, the `skill_setup` / `skill_executor` builtin agents, and the 16 skill agent tools | none (see below) | **Facade pattern (pathfinder for the other gates).** `pub mod voice;` is **always compiled** as a facade: the real submodules are `#[cfg(feature = "voice")]`, and a `#[cfg(not(feature = "voice"))] mod stub;` (`src/openhuman/voice/stub.rs`) re-exposes the same public surface that always-on / other-gated callers use (`server`, `dictation_listener`, `streaming`, `reply_speech`, `cloud_transcribe`, `cli`, `create_stt_provider`, `effective_stt_provider`, `publish_ptt_transcript_committed`) with no-op / `None` / disabled-error bodies. Callers therefore do **not** need per-call `#[cfg]`. When voice is off: the voice/audio controllers are unregistered (unknown-method over `/rpc`, absent from `/schema`), the `audio_generate_podcast` agent tools are absent, and `openhuman voice` returns a "voice disabled" error. Stub signatures must match the real ones exactly — the disabled build (`--no-default-features --features tokenjuice-treesitter`) is the **only** thing that catches drift, so run it before pushing any change to the voice surface. **Scope note:** the `voice` gate does **not** drop `whisper-rs` / `llama` / `cpal`. Those live in the inference domain (`src/openhuman/inference/local/service/whisper_engine.rs`; `cpal` is shared with accessibility) and await a separate future `inference` gate. The issue-level DoD line claiming whisper is dropped is superseded by this scope correction. +**`skills` gate — the type carve-out (read before adding the next gate).** The three skill domains follow the same facade+stub shape as `voice`, with one important refinement: **`skills` is not a leaf — it is partly load-bearing infrastructure.** `src/openhuman/tools/traits.rs` re-exports the crate's unified `ToolResult` / `ToolContent` out of `skills::types`, and ~236 files consume them (`mcp_client`, `runtime_node`, every `Tool` impl). `Workflow` / `WorkflowFrontmatter` / `WorkflowScope` from `skills::ops_types` likewise appear in always-on agent-harness and prompt signatures. Gating `skills` wholesale would take down the entire tool trait system, MCP, and the Node runtime. + +So `skills::types` and `skills::ops_types` stay **compiled in both directions** — they are inert serde/std-only definitions with zero coupling to their gated siblings — and only *behaviour* is gated. `src/openhuman/skills/stub.rs` therefore mirrors **functions only** and re-exports the real types (`pub use super::ops_types::{Workflow, …}`), so there is **zero type duplication** — strictly less drift surface than the `voice` stub, which had to re-declare `SttResult` + the `SttProvider` trait because those live inside its gated tree. + +> **Generalizable rule for the remaining gates:** put a domain's inert types in a dep-free submodule and leave it **ungated**; stub only the behaviour. Reach for a stub type only when the type genuinely cannot be carved out. + +Two places the carve-out doesn't reach, and why they are `#[cfg]` at the call site instead of stubbed: + +- `agent_registry/agents/loader.rs` — the `skill_setup` / `skill_executor` `BuiltinAgent` entries. `include_str!` embeds the agent TOML from disk regardless of module gating, so the entry itself must disappear. +- `agent/task_dispatcher/executor.rs` — the workflow-resolution branch. `registry::get_workflow` returns `Option`, which flattens in `AgentDefinition` and is destructured at the call site; stubbing it would mean re-declaring that struct (exactly what the carve-out avoids). With the domain compiled out no handle can resolve to a skill, so falling through to the builtin-agent branch is correct, not degraded. + +**Dep note:** `skills = []` — the empty list is **intentional, do not "fix" it**. Unlike `voice` (`hound`/`lettre`), these domains have no exclusive dependencies: every crate they touch is shared with always-on domains, and `runtime_node` / `runtime_python` are used by Agent / Flows / Memory too. This gate's value is tool-surface + prompt-bloat + startup cost, **not** binary size. + +When skills are off: the `skills` / `skill_runtime` / `skill_registry` controllers are unregistered (unknown-method over `/rpc`, absent from `/schema`), the 16 skill agent tools (incl. `run_workflow` / `await_workflow`) are **absent** from the tool list rather than degraded to an error, the `skill_setup` / `skill_executor` builtin agents are gone, and the boot-time remote catalog refresh is skipped. Composes with the runtime `DomainSet::skills` flag (#4796) — that axis needed no change here; #4798 is compile-time only. + ### Event bus (`src/core/event_bus/`) Typed pub/sub + native request/response. Both singletons — use module-level functions. From 39efdfd03c45c90a0f5618eb7a755579df042b3a Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:44:18 +0530 Subject: [PATCH 10/12] test(agent): cfg skills-dependent harness tests for the disabled build (#4798) Three tests hard-assert skills-domain behaviour and break only under --no-default-features, which CI never test-runs (the smoke lane is cargo check, which does not compile test code): - definition_tests: the effective-max-iterations table panics with "missing built-in agent definition: skill_executor" once the skill_setup/skill_executor BuiltinAgent entries are gated out. - session::tests refresh_workflows_{picks_up,retracts}_skill_*: both exercise real SKILL.md discovery from disk, which the disabled facade answers with an empty catalog by design. Found by sweeping the full disabled --lib suite rather than the scoped core::all filter; the mcp_agent/mcp_setup rows in the same table belong to #4799 and are left untouched. --- src/openhuman/agent/harness/definition_tests.rs | 4 ++++ src/openhuman/agent/harness/session/tests.rs | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/src/openhuman/agent/harness/definition_tests.rs b/src/openhuman/agent/harness/definition_tests.rs index ed905d26dc..d6d8ebc0f7 100644 --- a/src/openhuman/agent/harness/definition_tests.rs +++ b/src/openhuman/agent/harness/definition_tests.rs @@ -376,6 +376,8 @@ fn all_builtin_agent_definitions_have_expected_effective_max_iterations() { ("tools_agent", 50), ("flow_discovery", 50), ("workflow_builder", 50), + // Compiled out with the `skills` gate — see `openhuman::skills::stub`. + #[cfg(feature = "skills")] ("skill_executor", 50), ("tinyplace_agent", 50), ("subconscious", 30), @@ -404,6 +406,8 @@ fn all_builtin_agent_definitions_have_expected_effective_max_iterations() { ("vision_agent", 6), // Unchanged. ("presentation_agent", 10), + // Compiled out with the `skills` gate — see `openhuman::skills::stub`. + #[cfg(feature = "skills")] ("skill_setup", 10), ]; diff --git a/src/openhuman/agent/harness/session/tests.rs b/src/openhuman/agent/harness/session/tests.rs index 6f7b655c9d..96c35f8a8c 100644 --- a/src/openhuman/agent/harness/session/tests.rs +++ b/src/openhuman/agent/harness/session/tests.rs @@ -485,7 +485,11 @@ fn skill_listener_closed_channel_nulls_rx_and_is_not_a_signal() { ); } +/// Exercises real SKILL.md discovery from disk, so it is meaningful only with +/// the `skills` domain compiled in — the disabled facade's +/// `load_workflow_metadata` always returns an empty catalog by design. #[test] +#[cfg(feature = "skills")] fn refresh_workflows_picks_up_skill_installed_on_disk() { use crate::openhuman::skills::ops_types::{SKILL_MD, TRUST_MARKER}; @@ -551,7 +555,10 @@ fn refresh_workflows_picks_up_skill_installed_on_disk() { ); } +/// See [`refresh_workflows_picks_up_skill_installed_on_disk`] — same +/// disk-discovery dependency, so same `skills` gate. #[test] +#[cfg(feature = "skills")] fn refresh_workflows_retracts_skill_removed_from_disk() { use crate::openhuman::skills::ops_types::{SKILL_MD, TRUST_MARKER}; From cb274f28dba3b0f518366a6a81745bd784ebeb3e Mon Sep 17 00:00:00 2001 From: oxoxDev Date: Wed, 15 Jul 2026 17:44:53 +0530 Subject: [PATCH 11/12] =?UTF-8?q?revert(ci):=20drop=20the=20ci-lite=20step?= =?UTF-8?q?=20rename=20=E2=80=94=20#4797=20owns=20that=20line=20(#4798)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No functional change either way: the smoke lane's `--no-default-features --features tokenjuice-treesitter` is an explicit ALLOW-LIST, so the new default-ON `skills` gate is already exercised in its disabled direction with no workflow edit at all. #4797 renamed the same step first and logged it in the coordination Conflict Log; dropping our identical-intent rename leaves a conflict-free merge. The allow-list rationale is documented durably in AGENTS.md instead. --- .github/workflows/ci-lite.yml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/.github/workflows/ci-lite.yml b/.github/workflows/ci-lite.yml index 8572f9f385..870276ae6d 100644 --- a/.github/workflows/ci-lite.yml +++ b/.github/workflows/ci-lite.yml @@ -382,11 +382,7 @@ jobs: cache-on-failure: true shared-key: pr-rust-feature-gate-smoke - # Allow-list, not a deny-list: only `tokenjuice-treesitter` is enabled, so - # EVERY default-ON domain gate (voice, skills, …) is exercised in its - # disabled direction here. New gates are covered automatically — no new - # lane needed. This is the only thing that catches stub/facade drift. - - name: Check core builds with every domain gate disabled + - name: Check core builds with the voice gate disabled run: bash scripts/ci-cancel-aware.sh cargo check --manifest-path Cargo.toml --no-default-features --features tokenjuice-treesitter rust-core-coverage: From aec00afe740668746ce2df102777cb64a71bbb5d Mon Sep 17 00:00:00 2001 From: M3gA-Mind Date: Thu, 16 Jul 2026 13:55:27 +0530 Subject: [PATCH 12/12] docs(skills): correct stub surface list in facade module doc The skills disabled-facade stub deliberately has no `tools` module (the `skills::tools` glob is `#[cfg(feature = "skills")]` at its re-export site in `tools/mod.rs`, mirroring the voice gate). The module doc listed `tools` as part of the stub surface, which was inaccurate. Addresses CodeRabbit review. --- src/openhuman/skills/mod.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/openhuman/skills/mod.rs b/src/openhuman/skills/mod.rs index 41994801ae..9d24a2923a 100644 --- a/src/openhuman/skills/mod.rs +++ b/src/openhuman/skills/mod.rs @@ -6,9 +6,10 @@ //! submodules below are gated behind the default-ON `skills` Cargo feature; //! when it is off, [`stub`] takes their place and exposes the same public //! surface that always-on callers depend on (`load_workflow_metadata`, -//! `init_workflows_dir`, `registry`, `bus`, `tools`, the controller -//! aggregators) with no-op / empty bodies. Callers therefore do **not** need -//! per-call `#[cfg]`. +//! `init_workflows_dir`, `registry`, `bus`, the controller aggregators) with +//! no-op / empty bodies. Callers therefore do **not** need per-call `#[cfg]`. +//! (The `tools` glob is `#[cfg(feature = "skills")]` at its re-export site in +//! `tools/mod.rs`, so the stub omits it — mirroring the `voice` gate.) //! //! ### Type carve-out (why `types` + `ops_types` are NOT gated) //!