Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ model::WorkflowGraph → validate → compiler::compile → engine::run
- `validate.rs` — structural validation, run before compile.
- `caps/` — host-injected capability traits (`caps/mod.rs`); `caps/mock.rs` has
in-memory mock impls, gated behind the `mock` feature (always on inside tests).
`caps/host/` has *real* implementations a host may opt into — out-of-process
script/shell running, a file-backed `StateStore`, an allowlisted HTTP client —
behind the `host-caps` feature. These are offered, never assumed: nothing in
the engine reaches into them, and a host with a sandbox implements its own.
- `store/` — the durable model *around* a graph (versioned documents, run
records, notes, proposals), a JSON file-backed store for it, and `authoring`
(patch-based editing: apply → validate → gate → save), behind the `store`
feature. Not part of the engine: `engine::run` neither reads nor writes any of
it. `store::HostPolicy` is where a host injects the judgements only it can
make — which harnesses exist, which slugs resolve.
- `bindings.rs` — reading the `={{ ... }}` bindings a graph declares: which node
an expression reads from, and whether it reads as prose rather than jq.
- `gates/` — authoring gates: what is *guaranteed* wrong with a graph, refused
before a write rather than surfacing as a silent null at run time. Only the
host-agnostic ones; a host adds its own via `store::HostPolicy::check_graph`.
- `nodes/` — `NodeExecutor` trait + dispatch; `control_flow.rs` (if/switch/merge/
split_out/…) and `integration.rs` (agent/tool_call/http_request/code/…).
- `compiler.rs` — compiles a validated graph into runnable form.
Expand All @@ -44,6 +59,9 @@ model::WorkflowGraph → validate → compiler::compile → engine::run
- **Host-agnostic rule:** never hard-code an LLM/tool/HTTP/persistence vendor in
the crate. New outside-world effects go through a `caps` trait, not a direct
dependency. This is the core design constraint — do not violate it.
`caps/host/` and `store/` do not weaken it: they are *optional* implementations
behind default-off features, and the engine never depends on them. A host name
(`medulla`, `openhuman`) must not appear in either.
- **Declarative model:** no arbitrary embedded scripting in the workflow model;
code execution is a sandboxed capability, not model logic.
- **License:** GPL-3.0-or-later. Keep new files compatible.
Expand All @@ -52,8 +70,10 @@ model::WorkflowGraph → validate → compiler::compile → engine::run

```bash
cargo check # fast type/borrow check
cargo test # unit + compiler tests (mocks auto-available)
cargo test # unit + compiler tests (all optional modules compile in tests)
cargo test --features mock # exercise the mock capabilities explicitly
cargo check --features host-caps # the opt-in host capability implementations
cargo check --features store # the file-backed workflow/run store
cargo clippy --all-targets # lint
cargo fmt # format (run before committing)
cargo build --release
Expand Down
48 changes: 47 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 45 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,30 @@ thiserror = "2"
futures-timer = "3"
futures-util = "0.3"
getrandom = "0.4"
tinyagents = { version = "2.1", path = "vendor/tinyagents" }
# A registry coordinate, redirected to the vendored copy by the
# `[patch.crates-io]` table at the bottom of this file — NOT a `path`
# dependency, though the vendored tree is exactly what both resolve to when this
# crate is the workspace root.
#
# The difference only shows when something *embeds* this crate. A path
# dependency names a directory and cannot be redirected: `[patch.crates-io]` has
# no effect on it. So an embedding host that vendors its own `tinyagents` — and
# both known hosts do — ends up with two `tinyagents v2.1.0` packages at two
# paths, which Cargo refuses outright ("package collision in the lockfile")
# rather than resolving. Declared this way, a host's own patch table redirects
# this dependency along with its own and the graph holds one copy.
tinyagents = "2.1"
tracing = "0.1"
tokio = { version = "1", features = ["macros", "rt-multi-thread", "net", "signal", "sync", "time"] }
axum = { version = "0.8", features = ["ws"] }
reqwest = { version = "0.13", default-features = false, features = ["json"] }
# `caps::host` only: hashing an author-supplied state key (and namespace) into
# one safe path component, and staging a script in a temporary directory.
sha2 = { version = "0.10", optional = true }
tempfile = { version = "3", optional = true }
# `store` only: the advisory file lock that keeps two processes from deciding
# the same proposal at once.
fs2 = { version = "0.4", optional = true }
jaq-core = "3.1.0"
jaq-std = "3.0.1"
jaq-json = { version = "2.0.1", features = ["serde"] }
Expand All @@ -44,7 +63,32 @@ jaq-json = { version = "2.0.1", features = ["serde"] }
# and examples. Mocks are always available inside this crate's own tests.
default = []
mock = []
# Off by default; enables `caps::host` — ready-made capability implementations
# for a host that runs scripts as child processes of itself and keeps state on
# its own disk. Off by default because a host with a sandbox wants none of it,
# and because it is the only thing in the crate that spawns a process or writes
# a file. Always available inside this crate's own tests.
host-caps = ["dep:sha2", "dep:tempfile", "tokio/fs", "tokio/process", "tokio/io-util"]
# Off by default; enables `store` — the durable model around a graph (versioned
# documents, run records, notes, proposals) and a JSON file-backed store for it.
# A host with its own catalog or database wants none of it, so it is not part of
# the engine. Always available inside this crate's own tests.
store = ["dep:sha2", "dep:fs2"]

[dev-dependencies]
proptest = "1"
async-trait = "0.1"
# `caps::host` compiles inside this crate's own tests whether or not the
# `host-caps` feature is on (the same rule `caps::mock` follows), so what that
# feature would switch on has to be present for a test build too. An optional
# dependency is not activated by `cfg(test)`, hence these.
sha2 = "0.10"
tempfile = "3"
fs2 = "0.4"
tokio = { version = "1", features = ["fs", "process", "io-util"] }

# Applies only when this crate is the workspace root — an embedding host's own
# table wins, which is the point: standalone builds and tests use the vendored
# submodule, and a host redirects `tinyagents` to whichever copy it links.
[patch.crates-io]
tinyagents = { path = "vendor/tinyagents" }
Loading