# How the OpenHuman Skills Runtime Executes SKILL.md Workflows with Node and Python Clients

> Learn how the OpenHuman Skills runtime executes SKILL.md workflows using Node and Python clients. Discover how it converts files into running processes for seamless execution.

- Repository: [Tiny Humans/openhuman](https://github.com/tinyhumansai/openhuman)
- Tags: how-to-guide
- Published: 2026-08-24

---

**The OpenHuman Skills runtime converts SKILL.md files into running processes by discovering workflow metadata, resolving language-specific runtimes through Node and Python clients, spawning child processes with JSON inputs, and tracking execution via run IDs and log files.**

The Skills runtime is the execution engine within the OpenHuman repository that transforms static workflow descriptions into live automation tasks. It bridges the gap between declarative SKILL.md files and concrete process execution, managing everything from environment setup to log collection across Node.js and Python ecosystems.

## Discovering Workflow Metadata

Before execution begins, the runtime must locate and validate the target workflow. The **catalog** module in [`src/openhuman/skills/catalog/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/catalog/ops.rs) handles the discovery phase, parsing the installed [`SKILL.md`](https://github.com/tinyhumansai/openhuman/blob/main/SKILL.md) file to extract critical metadata including input schemas, declared language runtimes, script entry points, and resource requirements.

The **registry** at [`src/openhuman/skills/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/registry.rs) maintains a centralized index of all installed skills. When a user invokes a workflow by its ID, the runtime queries this registry to resolve the skill's location and configuration before proceeding to the execution phase.

## Resolving Node and Python Runtimes

Once the workflow metadata is loaded, the runtime must prepare the appropriate execution environment. The system delegates this responsibility to specialized client crates that handle language-specific setup.

### Node Client Resolution

The **Node client** resides in the `runtime_node` crate ([`src/openhuman/runtime/node/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/runtime/node/mod.rs)). Its primary function is to locate the `node`, `npm`, and `npx` binaries on the host system, inject custom `PATH` variables, and ensure missing packages are installed before execution.

### Python Client Resolution

The **Python client** lives in the `runtime_python` crate ([`src/openhuman/runtime/python/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/runtime/python/mod.rs)). It resolves the Python interpreter path, creates virtual environments when necessary, and validates that required packages are available in the target environment.

Both clients expose the function `resolve_runtime(runtime: RuntimeKind) -> Result<RuntimeInfo>` which returns the absolute binary path and environment variables required for execution. The Skills runtime calls this from [`src/openhuman/skills/runtime/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/ops.rs) when a workflow declares `runtime: "node"`, `runtime: "python"`, or `"all"` to allow either interpreter.

## Spawning Workflow Processes

The actual process creation logic lives in [`src/openhuman/skills/runtime/run_machinery.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/run_machinery.rs). The async function `spawn_workflow_run_background` orchestrates the execution pipeline:

```rust
pub async fn spawn_workflow_run_background(
    skill_id: &str,
    inputs: serde_json::Value,
    runtime: RuntimeKind,
) -> Result<RunHandle> {
    // 1️⃣ Resolve the runtime (Node / Python)
    let runtime_info = resolve_runtime(runtime).await?;
    // 2️⃣ Build the command
    let mut cmd = std::process::Command::new(runtime_info.binary);
    cmd.args(&runtime_info.args);
    // 3️⃣ Pipe the serialized inputs
    cmd.stdin(std::process::Stdio::piped());
    // 4️⃣ Launch the child process
    let child = cmd.spawn()?;
    // 5️⃣ Track the run via a RunHandle (log path, IDs, status)
    // ...
}

```

For **Node** workflows, the runtime invokes `runtime_node::node::spawn_process` to construct a `std::process::Command` with the resolved binary, inject the skill's working directory, append `npm` arguments, and forward JSON-encoded inputs via `stdin`.

For **Python** workflows, `runtime_python::python::spawn_process` performs equivalent steps with the resolved Python binary, optionally activating a virtual environment before passing inputs to the script.

## Tracking Execution and Collecting Results

Every workflow execution receives a unique **run ID** and dedicated log file written to `<workspace>/skill_runtime/logs/`. When a process launches, the runtime emits a `WorkflowRunStarted` event (defined in [`run_machinery.rs`](https://github.com/tinyhumansai/openhuman/blob/main/run_machinery.rs)) containing the `run_id`, `workflow_id`, and log path.

A background task monitors the child process, streaming `stdout` and `stderr` to the log file and posting progress events on the internal event bus (`src/core/event_bus/`). Upon completion, the `await_run_outcome` function (also in [`run_machinery.rs`](https://github.com/tinyhumansai/openhuman/blob/main/run_machinery.rs)) captures the exit status, parses any JSON result emitted by the script, and publishes a `WorkflowRunCompleted` event.

Users can query recent executions via `skill_runtime recent_runs` or inspect specific logs with `skill_runtime view_log`.

## RPC Interface and Skill Executor Agent

The **Skill Executor agent** at [`src/openhuman/skills/runtime/agent/skill_executor/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/agent/skill_executor/mod.rs) exposes the runtime functionality through RPC calls. This embedded agent registers the `skill_runtime` namespace within the core process (listed in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs)), handling requests such as `skill_runtime run`, `skill_runtime cancel`, and `skill_runtime recent_runs` through the high-level operation handlers in [`src/openhuman/skills/runtime/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/ops.rs).

## Practical Usage Examples

### CLI Execution

List installed workflows and resolve available runtimes:

```bash

# Display all installed SKILL.md workflows

openhuman skill_runtime list

# Verify Node and Python runtime availability

openhuman skill_runtime resolve_runtimes --runtime all

```

Execute specific skills with JSON inputs:

```bash

# Run a Node-based skill

openhuman skill_runtime run --skill_id git-helper \
    --inputs '{"repo":"my-repo","branch":"main"}'

# Run a Python-based skill

openhuman skill_runtime run --skill_id data-cleaner \
    --inputs '{"path":"./data.csv"}' \
    --runtime python

```

Inspect execution history:

```bash

# Show the ten most recent runs

openhuman skill_runtime recent_runs --limit 10

# Stream logs for a specific execution

openhuman skill_runtime view_log --run_id 2024-08-24-01-23-45-abcdef

```

### Programmatic Execution

Trigger workflows from Rust code using the public API exported from [`src/openhuman/skills/runtime/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/mod.rs):

```rust
use openhuman_core::skills::runtime::{run_skill, RuntimeKind};

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Execute a Node workflow
    let handle = run_skill(
        "git-helper",
        serde_json::json!({ "repo": "my-repo", "branch": "main" }),
        RuntimeKind::Node,
    )
    .await?;

    // Await completion and retrieve output
    let result = handle.await_result().await?;
    println!("Skill finished with output: {}", result);
    Ok(())
}

```

## Summary

- The Skills runtime discovers [`SKILL.md`](https://github.com/tinyhumansai/openhuman/blob/main/SKILL.md) workflows through [`src/openhuman/skills/catalog/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/catalog/ops.rs) and the central registry at [`src/openhuman/skills/registry.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/registry.rs).
- **Node** and **Python** runtimes are resolved via dedicated crates that locate binaries, manage virtual environments, and prepare execution contexts via `resolve_runtime()` in [`src/openhuman/skills/runtime/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/ops.rs).
- Process spawning occurs in [`src/openhuman/skills/runtime/run_machinery.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/run_machinery.rs) through `spawn_workflow_run_background`, which pipes JSON inputs via `stdin` and returns a `RunHandle` for tracking.
- Execution lifecycle events (`WorkflowRunStarted`, `WorkflowRunCompleted`) and logs are managed through the internal event bus and written to `<workspace>/skill_runtime/logs/`.
- The **Skill Executor agent** exposes all functionality through the `skill_runtime` RPC namespace, registered in [`src/core/all.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/core/all.rs).

## Frequently Asked Questions

### How does the Skills runtime locate the correct Node or Python interpreter?

The runtime delegates resolution to the `runtime_node` and `runtime_python` crates. The function `resolve_runtime(runtime: RuntimeKind)` in [`src/openhuman/skills/runtime/ops.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/ops.rs) queries these helpers to find absolute binary paths, check for `npm`/`npx` availability, create Python virtual environments, and return the environment variables required for execution.

### What happens to the JSON inputs provided to a SKILL.md workflow?

Inputs are serialized as JSON and passed directly to the spawned process via `stdin`. In [`src/openhuman/skills/runtime/run_machinery.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/run_machinery.rs), the `spawn_workflow_run_background` function pipes `serde_json::Value` into the child process before execution begins, allowing scripts to read and parse the configuration immediately upon startup.

### Where does OpenHuman store logs and execution metadata for workflow runs?

Each execution receives a unique run ID and writes logs to `<workspace>/skill_runtime/logs/`. The runtime records metadata through `WorkflowRunStarted` and `WorkflowRunCompleted` events posted to the internal event bus (`src/core/event_bus/`), while the `await_run_outcome` function in [`run_machinery.rs`](https://github.com/tinyhumansai/openhuman/blob/main/run_machinery.rs) finalizes the run status and parses result data.

### How can I programmatically trigger a skill execution from Rust code?

Import the runtime module from [`src/openhuman/skills/runtime/mod.rs`](https://github.com/tinyhumansai/openhuman/blob/main/src/openhuman/skills/runtime/mod.rs) and call the `run_skill()` function with the skill ID, JSON inputs, and `RuntimeKind::Node` or `RuntimeKind::Python`. This returns a `RunHandle` that allows you to await completion and retrieve the final output through `handle.await_result().await`.