polyc-tools 2026.9.0

The in-process tool core for polychrome agents: local executors (coding, web fetch, wallet, ...), the tool registry, and MCP composition. The networked connectors live in polyc-connectors.
//! `#2295` — a delegated worker can read the parent files its task names, and
//! nothing else.
//!
//! `#2286` fenced every worker into its own workspace subtree and re-rooted
//! the coding tools against it, reads included, so a worker could no longer
//! read the parent's workspace at all — `worker_write_fencing`'s
//! `worker_cannot_read_a_file_the_parent_wrote` pins that loss deliberately.
//! These tests pin the narrow reach given back: the target agent's ceiling
//! bounds what is reachable, the `__delegate_to` call names paths inside it,
//! and the worker gets its own COPY at the same relative path.
//!
//! The fence has to survive the seeding, so the `#2286` invariants are
//! asserted here too, against seeded workers rather than empty ones: siblings
//! still cannot clobber each other, and a worker's edit to a seeded file never
//! reaches the parent's original.

#![allow(clippy::pedantic, clippy::nursery, missing_docs)]

use std::sync::atomic::{AtomicU64, Ordering};

use polyc_agent::ToolExecutor;
use polyc_agent::delegate::{ShareInCeiling, ShareInError, WorkerScope};
use polyc_tools::ToolRegistry;

/// A unique temp dir per test (atomic counter, not wall-clock — safe under
/// parallel test threads). Mirrors `worker_write_fencing`'s helper; the
/// crate-internal one is `#[cfg(test)]` and so invisible here.
fn tmp_root(prefix: &str) -> std::path::PathBuf {
    static SEQ: AtomicU64 = AtomicU64::new(0);
    let mut p = std::env::temp_dir();
    let n = SEQ.fetch_add(1, Ordering::Relaxed);
    p.push(format!("pc-{prefix}-{}-{n}", std::process::id()));
    std::fs::create_dir_all(&p).expect("temp root is creatable");
    p
}

/// A registry rooted at an explicit directory. Never touches process env —
/// this workspace forbids `std::env::set_var` in tests.
fn registry_at(root: &std::path::Path) -> ToolRegistry {
    ToolRegistry::rooted_at(root.to_path_buf(), None)
}

/// An open-enough ceiling for the happy paths: everything under `src`, a
/// handful of files, a few kilobytes.
fn src_ceiling() -> ShareInCeiling {
    ShareInCeiling {
        allow: vec!["src/**".to_owned()],
        max_files: 8,
        max_bytes: 4096,
    }
}

async fn write_file(tools: &dyn ToolExecutor, path: &str, content: &str) -> String {
    let args = serde_json::json!({ "path": path, "content": content }).to_string();
    tools.execute("file_write", &args).await
}

async fn read_file(tools: &dyn ToolExecutor, path: &str) -> String {
    let args = serde_json::json!({ "path": path }).to_string();
    tools.execute("file_read", &args).await
}

/// Re-root `parent` for one worker, seeding `share_in` under `ceiling`.
fn worker(
    parent: &ToolRegistry,
    id: &str,
    share_in: &[String],
    ceiling: &ShareInCeiling,
) -> Result<(std::sync::Arc<dyn ToolExecutor>, Vec<String>), ShareInError> {
    let scope = WorkerScope {
        worker_id: id,
        share_in,
        ceiling,
    };
    parent
        .for_worker(&scope)
        .expect("a registry owns a workspace, so it must re-root")
        .map(|handoff| (handoff.tools, handoff.seeded))
}

/// The refusal from a delegation that must fail. A plain `expect_err` can't be
/// used: the success side carries a `dyn ToolExecutor`, which has no `Debug`.
fn refusal(
    result: Result<(std::sync::Arc<dyn ToolExecutor>, Vec<String>), ShareInError>,
) -> ShareInError {
    match result {
        Ok((_, seeded)) => {
            panic!("expected the delegation to be refused, but it seeded {seeded:?}")
        }
        Err(err) => err,
    }
}

/// THE regression test for `#2295`: the delegation "review the file I just
/// wrote" has to work.
///
/// Before this, the worker's `file_read` resolved against its own empty
/// scratch directory and returned nothing, so the worker answered about a file
/// it never saw.
#[tokio::test]
async fn a_worker_reads_a_parent_file_the_call_named() {
    let root = tmp_root("share-in-reads");
    let parent = registry_at(&root);
    write_file(&parent, "src/parser.rs", "fn parse() -> u8 { 42 }").await;

    let (worker, seeded) = worker(
        &parent,
        "call-a",
        &["src/parser.rs".to_owned()],
        &src_ceiling(),
    )
    .expect("a file inside the ceiling seeds");

    assert_eq!(seeded, vec!["src/parser.rs".to_owned()]);
    let out = read_file(worker.as_ref(), "src/parser.rs").await;
    assert!(
        out.contains("fn parse"),
        "a seeded worker must see the parent's file: {out}"
    );

    std::fs::remove_dir_all(&root).ok();
}

/// The fence is intact everywhere the call did not reach: seeding one file
/// must not restore blanket read access to the parent's workspace.
#[tokio::test]
async fn an_unnamed_parent_file_stays_unreachable() {
    let root = tmp_root("share-in-unnamed");
    let parent = registry_at(&root);
    write_file(&parent, "src/parser.rs", "fn parse() {}").await;
    write_file(&parent, "src/secrets.rs", "const TOKEN: &str = \"s3cret\";").await;

    let (worker, _) = worker(
        &parent,
        "call-a",
        &["src/parser.rs".to_owned()],
        &src_ceiling(),
    )
    .expect("a file inside the ceiling seeds");

    let out = read_file(worker.as_ref(), "src/secrets.rs").await;
    assert!(
        !out.contains("s3cret"),
        "seeding one file must not expose the rest of the workspace: {out}"
    );

    std::fs::remove_dir_all(&root).ok();
}

/// Seeding hands over a COPY. A worker editing what it was given must never
/// reach the parent's original — otherwise share-in would be a back door into
/// exactly the clobbering `#2286` fenced off.
#[tokio::test]
async fn a_workers_edit_to_a_seeded_file_never_reaches_the_parent() {
    let root = tmp_root("share-in-copy");
    let parent = registry_at(&root);
    write_file(&parent, "src/parser.rs", "original").await;

    let (worker, _) = worker(
        &parent,
        "call-a",
        &["src/parser.rs".to_owned()],
        &src_ceiling(),
    )
    .expect("a file inside the ceiling seeds");
    write_file(worker.as_ref(), "src/parser.rs", "rewritten by the worker").await;

    let parent_bytes =
        std::fs::read_to_string(root.join("src/parser.rs")).expect("parent's file survives");
    assert_eq!(
        parent_bytes, "original",
        "the worker wrote through its copy into the parent's file"
    );

    std::fs::remove_dir_all(&root).ok();
}

/// `#2286` must still hold with seeding in play: two workers handed the SAME
/// parent file still write to two different places.
#[tokio::test]
async fn two_seeded_workers_still_cannot_clobber_each_other() {
    let root = tmp_root("share-in-concurrent");
    let parent = registry_at(&root);
    write_file(&parent, "src/parser.rs", "original").await;

    let requested = vec!["src/parser.rs".to_owned()];
    let (a, _) = worker(&parent, "call-a", &requested, &src_ceiling()).expect("seeds");
    let (b, _) = worker(&parent, "call-b", &requested, &src_ceiling()).expect("seeds");

    tokio::join!(
        write_file(a.as_ref(), "src/parser.rs", "written-by-a"),
        write_file(b.as_ref(), "src/parser.rs", "written-by-b"),
    );

    assert!(
        read_file(a.as_ref(), "src/parser.rs")
            .await
            .contains("written-by-a"),
        "worker a lost its write to worker b"
    );
    assert!(
        read_file(b.as_ref(), "src/parser.rs")
            .await
            .contains("written-by-b"),
        "worker b lost its write to worker a"
    );

    std::fs::remove_dir_all(&root).ok();
}

/// The ceiling is enforced at the real seam, not only in the seeding unit:
/// a path the target agent never declared fails the delegation.
#[tokio::test]
async fn the_registry_refuses_a_path_outside_the_ceiling() {
    let root = tmp_root("share-in-ceiling");
    let parent = registry_at(&root);
    write_file(&parent, "secrets/token", "s3cret").await;

    let err = refusal(worker(
        &parent,
        "call-a",
        &["secrets/token".to_owned()],
        &src_ceiling(),
    ));
    // a path outside the ceiling must fail the delegation

    assert!(
        matches!(err, ShareInError::OutsideCeiling { .. }),
        "{err:?}"
    );

    std::fs::remove_dir_all(&root).ok();
}

/// An agent that never declared `delegateShareIn` keeps `#2286`'s fully
/// isolated worker, even when the model asks for a file.
#[tokio::test]
async fn a_worker_of_an_unconfigured_agent_seeds_nothing() {
    let root = tmp_root("share-in-closed");
    let parent = registry_at(&root);
    write_file(&parent, "src/parser.rs", "fn parse() {}").await;

    let err = refusal(worker(
        &parent,
        "call-a",
        &["src/parser.rs".to_owned()],
        &ShareInCeiling::default(),
    ));
    // a closed ceiling admits nothing

    assert!(
        matches!(err, ShareInError::OutsideCeiling { .. }),
        "{err:?}"
    );

    std::fs::remove_dir_all(&root).ok();
}

/// A worker naming a sibling's scratch directory is refused. Without this the
/// share-in path would hand one worker another's files — the cross-worker read
/// `#2286` exists to prevent, reintroduced through the seeding door.
#[tokio::test]
async fn a_worker_cannot_seed_from_a_siblings_subtree() {
    let root = tmp_root("share-in-sibling");
    let parent = registry_at(&root);

    let (sibling, _) = worker(&parent, "call-b", &[], &src_ceiling()).expect("re-roots");
    write_file(sibling.as_ref(), "notes.md", "the sibling's private work").await;

    // Find the sibling's real subdirectory name to aim at it precisely.
    let sibling_dir = std::fs::read_dir(&root)
        .expect("root readable")
        .flatten()
        .map(|e| e.file_name().to_string_lossy().into_owned())
        .find(|name| name.starts_with(".worker-"))
        .expect("the sibling's subtree exists on disk");

    let err = refusal(worker(
        &parent,
        "call-a",
        &[format!("{sibling_dir}/notes.md")],
        &ShareInCeiling {
            allow: vec!["**".to_owned()],
            max_files: 8,
            max_bytes: 4096,
        },
    ));
    // a sibling worker's subtree is never seedable, even under `**`

    assert!(matches!(err, ShareInError::WorkerSubtree { .. }), "{err:?}");

    std::fs::remove_dir_all(&root).ok();
}

/// A worker that requested nothing behaves exactly as it did before `#2295`.
#[tokio::test]
async fn an_unseeded_worker_is_unchanged_from_the_fenced_behavior() {
    let root = tmp_root("share-in-bare");
    let parent = registry_at(&root);
    write_file(&parent, "src/parser.rs", "fn parse() {}").await;

    let (worker, seeded) = worker(&parent, "call-a", &[], &src_ceiling()).expect("re-roots");

    assert!(seeded.is_empty());
    let out = read_file(worker.as_ref(), "src/parser.rs").await;
    assert!(
        !out.contains("fn parse"),
        "a worker that asked for nothing must see nothing: {out}"
    );

    std::fs::remove_dir_all(&root).ok();
}