fluers-runtime 0.6.0

The Fluers agent harness: agent definition, sessions, skills, sandbox, events
Documentation
//! Embedding-first dependency guard (WP-3).
//!
//! Asserts that `fluers-core` and `fluers-runtime` — the embedding surface
//! fae consumes in-process — pull **none** of the forbidden network/server/db
//! crates (`reqwest`, `hyper`, `axum`, `sqlx`, the `opentelemetry` family),
//! directly or transitively. Adapters (`fluers-server`, `fluers-postgres`,
//! `fluers-otel`, …) MAY use these; the core/runtime path must not, so the
//! embedding-first constraint cannot regress silently.
//!
//! The guard parses `cargo metadata --locked` at test time (no extra
//! dependency) and walks the resolve graph from the two guarded roots,
//! following normal and build edges but **not** dev-only edges (a legitimate
//! test-only dependency that transitively pulls a forbidden crate must not
//! trip the guard). The walk is keyed by package id, not name, so a name
//! shared by unrelated package nodes cannot merge their dependency sets.
//!
//! Falsifiability: temporarily add e.g. `reqwest.workspace = true` to
//! `crates/fluers-runtime/Cargo.toml`, run `cargo test -p fluers-runtime
//! --test embedding_dep_guard`, confirm failure, then revert. (Verified at
//! WP-3 time for both a direct add and a transitive-only add via
//! `fluers-providers`; see the PR body.)

#![cfg(test)]
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]

use std::collections::{HashMap, HashSet};
use std::process::Command;

/// Crates whose dependency trees are guarded (the embedding surface).
const GUARDED_ROOTS: &[&str] = &["fluers-core", "fluers-runtime"];

/// Forbidden crate name stems. A forbidden crate matches if its name equals
/// the stem or starts with `stem` followed by `-` or `_` (covers the
/// `opentelemetry-*` / `opentelemetry_*` family and `hyper-util` style splits).
const FORBIDDEN_STEMS: &[&str] = &["reqwest", "hyper", "axum", "sqlx", "opentelemetry"];

/// True if a package name is forbidden under any stem above.
fn is_forbidden(name: &str) -> bool {
    FORBIDDEN_STEMS.iter().any(|stem| {
        name == *stem
            || name
                .strip_prefix(stem)
                .is_some_and(|rest| rest.starts_with('-') || rest.starts_with('_'))
    })
}

#[test]
fn fluers_core_and_runtime_have_no_forbidden_dependencies() {
    let metadata = cargo_metadata();

    // id -> crate name (for reporting and for starting the walk from a name).
    let id_to_name: HashMap<&str, &str> = metadata["packages"]
        .as_array()
        .expect("`packages` is an array")
        .iter()
        .map(|p| {
            (
                p["id"].as_str().expect("package id"),
                p["name"].as_str().expect("package name"),
            )
        })
        .collect();

    // Resolve node id -> set of dependency node ids, following normal/build
    // edges only (dev-only edges are skipped via `dep_kinds`). id-keyed so a
    // name shared by unrelated package nodes does not merge their dep sets.
    let mut graph: HashMap<&str, HashSet<String>> = HashMap::new();
    for node in metadata["resolve"]["nodes"]
        .as_array()
        .expect("resolve `nodes` is an array")
    {
        let node_id = node["id"].as_str().expect("node id");
        let deps = graph.entry(node_id).or_default();
        for dep in node["deps"].as_array().expect("node `deps` is an array") {
            let has_prod_edge = dep["dep_kinds"]
                .as_array()
                .is_some_and(|kinds| kinds.iter().any(|k| k["kind"].as_str() != Some("dev")));
            if !has_prod_edge {
                continue;
            }
            deps.insert(dep["pkg"].as_str().expect("dep pkg id").to_string());
        }
    }

    // Transitive closure per guarded root: BFS over the id-keyed graph,
    // starting from every resolve node whose package name is a guarded root.
    let mut failures: Vec<String> = Vec::new();
    for root in GUARDED_ROOTS {
        let start_ids: Vec<&str> = id_to_name
            .iter()
            .filter(|(_, name)| **name == *root)
            .map(|(id, _)| *id)
            .collect();
        let mut seen: HashSet<String> = HashSet::new();
        let mut stack: Vec<&str> = start_ids;
        let mut found_forbidden: Vec<String> = Vec::new();
        while let Some(id) = stack.pop() {
            if !seen.insert(id.to_string()) {
                continue;
            }
            let name = id_to_name.get(id).copied().unwrap_or("");
            if name != *root && is_forbidden(name) {
                found_forbidden.push(name.to_string());
                continue; // don't expand past a forbidden crate
            }
            if let Some(deps) = graph.get(id) {
                for d in deps {
                    stack.push(d.as_str());
                }
            }
        }
        if !found_forbidden.is_empty() {
            failures.push(format!(
                "{root} transitively depends on forbidden crates (prod/build edge): {}",
                found_forbidden.join(", ")
            ));
        }
    }

    // Also assert the guarded roots never list a forbidden crate directly on a
    // non-dev edge (catches a direct `[dependencies]` add even if the resolve
    // graph is stale). `packages[].dependencies[].kind` is `null` (normal),
    // `"build"`, or `"dev"`; we flag only non-dev.
    for root in GUARDED_ROOTS {
        let pkg = metadata["packages"]
            .as_array()
            .expect("`packages` is an array")
            .iter()
            .find(|p| p["name"].as_str() == Some(*root))
            .unwrap_or_else(|| panic!("{root}: package not found in cargo metadata"));
        let direct: Vec<String> = pkg["dependencies"]
            .as_array()
            .expect("package `dependencies` is an array")
            .iter()
            .filter(|d| d["kind"].as_str() != Some("dev"))
            .filter_map(|d| d["name"].as_str())
            .filter(|n| is_forbidden(n))
            .map(String::from)
            .collect();
        if !direct.is_empty() {
            failures.push(format!(
                "{root} directly declares forbidden crates: {}",
                direct.join(", ")
            ));
        }
    }

    assert!(
        failures.is_empty(),
        "embedding-first guard violated (WP-3):\n  - {}\n\
         These crates must not appear under fluers-core/fluers-runtime. \
         Adapters (server/postgres/otel) may use them; the embedding path may not.",
        failures.join("\n  - ")
    );
}

/// Run `cargo metadata --format-version 1 --locked` at the workspace root and
/// parse it. `--locked` requires an up-to-date `Cargo.lock` so the graph
/// walked is the one that will actually build, not a re-resolved approximation.
fn cargo_metadata() -> serde_json::Value {
    let cargo = std::env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
    let output = Command::new(cargo)
        .args(["metadata", "--format-version", "1", "--locked"])
        .current_dir(workspace_root())
        .output()
        .expect("failed to run `cargo metadata`");
    assert!(
        output.status.success(),
        "`cargo metadata` failed: {}",
        String::from_utf8_lossy(&output.stderr)
    );
    serde_json::from_slice(&output.stdout).expect("`cargo metadata` produced invalid JSON")
}

/// Walk up from this test file to the workspace root (the dir containing the
/// top-level `Cargo.toml` with a `[workspace]` table).
fn workspace_root() -> std::path::PathBuf {
    let mut dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    while dir.parent().is_some() {
        if dir.join("Cargo.toml").exists() {
            let manifest =
                std::fs::read_to_string(dir.join("Cargo.toml")).expect("read workspace Cargo.toml");
            if manifest.contains("[workspace]") {
                return dir;
            }
        }
        dir = dir
            .parent()
            .expect("reached filesystem root without finding workspace")
            .to_path_buf();
    }
    panic!("could not locate workspace root from CARGO_MANIFEST_DIR");
}