test: rename 'fake' test agent → 'sample' #159

Merged
clawdie merged 1 commit from rename-sample-test-agent into main 2026-06-24 00:37:38 +02:00
10 changed files with 60 additions and 52 deletions

View file

@ -700,7 +700,7 @@ mod tests {
assert_eq!(
parsed(&[
"spawn-local",
"/tmp/fake-agent",
"/tmp/sample-agent",
"--session-id",
"s1",
"--system-prompt",
@ -710,7 +710,7 @@ mod tests {
socket_path: default_socket_path(),
command: Command::SpawnAgent {
provider: "local".to_string(),
model: "/tmp/fake-agent".to_string(),
model: "/tmp/sample-agent".to_string(),
session_id: Some("s1".to_string()),
system_prompt: Some("hello".to_string()),
jail: None,
@ -847,7 +847,7 @@ mod tests {
assert_eq!(
parsed(&[
"spawn-local",
"/tmp/fake-agent",
"/tmp/sample-agent",
"--jail-name",
"proof0",
"--jail-root",
@ -857,7 +857,7 @@ mod tests {
socket_path: default_socket_path(),
command: Command::SpawnAgent {
provider: "local".to_string(),
model: "/tmp/fake-agent".to_string(),
model: "/tmp/sample-agent".to_string(),
session_id: None,
system_prompt: None,
jail: Some(colibri_client::JailConfig {

View file

@ -90,10 +90,10 @@ async fn wait_for_state(client: &DaemonClient, expected: AgentState) -> String {
}
#[tokio::test]
async fn daemon_client_live_socket_check_with_local_fake_agent() {
async fn daemon_client_live_socket_check_with_local_sample_agent() {
let config = check_config();
tokio::fs::create_dir_all(&config.data_dir).await.unwrap();
let fake_agent = env!("CARGO_BIN_EXE_colibri-test-agent");
let sample_agent = env!("CARGO_BIN_EXE_colibri-test-agent");
let state: SharedState = Arc::new(DaemonState::new(config.clone()));
let shutdown = state.shutdown_rx.resubscribe();
@ -113,7 +113,12 @@ async fn daemon_client_live_socket_check_with_local_fake_agent() {
assert!(empty_snapshot.panes.is_empty());
let spawn = client
.spawn_agent("local", fake_agent, Some("test-session".to_string()), None)
.spawn_agent(
"local",
sample_agent,
Some("test-session".to_string()),
None,
)
.await
.unwrap();
let agent_id = spawn["agent_id"].as_str().unwrap().to_string();
@ -202,12 +207,12 @@ async fn colibri_cli_task_commands_use_socket_api() {
#[tokio::test]
async fn poll_tasks_spawns_agent_for_claimed_task() {
let mut config = check_config();
let fake_agent = env!("CARGO_BIN_EXE_colibri-test-agent");
let sample_agent = env!("CARGO_BIN_EXE_colibri-test-agent");
// Set COLIBRI_AGENT_BINARY so poll_tasks uses the test agent binary
config.data_dir =
std::env::temp_dir().join(format!("colibri-poll-tasks-test-{}", Uuid::new_v4()));
tokio::fs::create_dir_all(&config.data_dir).await.unwrap();
std::env::set_var("COLIBRI_AGENT_BINARY", fake_agent);
std::env::set_var("COLIBRI_AGENT_BINARY", sample_agent);
let state: SharedState = Arc::new(DaemonState::new(config.clone()));
let shutdown = state.shutdown_rx.resubscribe();
@ -310,7 +315,7 @@ async fn poll_tasks_spawns_agent_for_claimed_task() {
async fn harness_double_spawn_session_isolation() {
let config = check_config();
tokio::fs::create_dir_all(&config.data_dir).await.unwrap();
let fake_agent = env!("CARGO_BIN_EXE_colibri-test-agent");
let sample_agent = env!("CARGO_BIN_EXE_colibri-test-agent");
let state: SharedState = Arc::new(DaemonState::new(config.clone()));
let shutdown = state.shutdown_rx.resubscribe();
@ -326,7 +331,7 @@ async fn harness_double_spawn_session_isolation() {
client
.spawn_agent(
"local",
fake_agent,
sample_agent,
Some("harness-session-a".to_string()),
None,
)
@ -335,7 +340,7 @@ async fn harness_double_spawn_session_isolation() {
client
.spawn_agent(
"local",
fake_agent,
sample_agent,
Some("harness-session-b".to_string()),
None,
)

View file

@ -1088,7 +1088,7 @@ mod tests {
let state: SharedState = Arc::new(DaemonState::new(test_config()));
state.glasspane.write().await.attach_pane_at(
"pane-a",
"fake-agent",
"sample-agent",
SystemTime::UNIX_EPOCH,
);
state.glasspane.write().await.ingest_line_at(
@ -1106,16 +1106,16 @@ mod tests {
}
#[tokio::test]
async fn fake_agent_stdout_stream_updates_glasspane() {
async fn sample_agent_stdout_stream_updates_glasspane() {
let state: SharedState = Arc::new(DaemonState::new(test_config()));
let pane_id = "fake-agent-pane".to_string();
let pane_id = "sample-agent-pane".to_string();
state.glasspane.write().await.attach_pane_at(
pane_id.clone(),
"fake-agent",
"sample-agent",
SystemTime::UNIX_EPOCH,
);
let script = r#"printf '%s\n' '{"type":"session","id":"pi-fake","cwd":"/tmp"}' '{"type":"turn_start"}' '{"type":"turn_end"}'"#;
let script = r#"printf '%s\n' '{"type":"session","id":"pi-sample","cwd":"/tmp"}' '{"type":"turn_start"}' '{"type":"turn_end"}'"#;
let mut child = Command::new(PathBuf::from("/bin/sh"))
.arg("-c")
.arg(script)
@ -1139,7 +1139,7 @@ mod tests {
.find(|pane| pane.id == pane_id)
.unwrap();
assert_eq!(pane.state, colibri_glasspane::AgentState::Done);
assert_eq!(pane.session_id.as_deref(), Some("pi-fake"));
assert_eq!(pane.session_id.as_deref(), Some("pi-sample"));
}
#[tokio::test]

View file

@ -9,7 +9,7 @@
//! 3. Anthropic (fallback)
//!
//! `Provider::Local` is intentionally excluded from fallback routing and API-key
//! checks. It exists for deterministic daemon/client startup checks with fake
//! checks. It exists for deterministic daemon/client startup checks with sample
//! Pi-JSONL agents and for future local-only tools.
use std::collections::HashMap;

View file

@ -1,7 +1,7 @@
//! Pi spawn path proof — integration test.
//!
//! Validates: Colibri spawns agent → reads JSONL stdout → glasspane ingests → snapshot correct.
//! Uses scripts/fake-pi-agent.py which emits the colibri-pi-events JSONL taxonomy.
//! Uses scripts/sample-pi-agent.py which emits the colibri-pi-events JSONL taxonomy.
//!
//! With real Pi binary (when installed):
//! COLIBRI_AGENT_BINARY=pi cargo test -p colibri-daemon --test pi_spawn_live -- --nocapture
@ -22,20 +22,23 @@ async fn pi_spawn_path_produces_correct_glasspane_state() {
.parent()
.unwrap()
.join("scripts")
.join("fake-pi-agent.py");
.join("sample-pi-agent.py");
assert!(script.exists(), "fake-pi-agent.py not found at {script:?}");
assert!(
script.exists(),
"sample-pi-agent.py not found at {script:?}"
);
let mut supervisor = colibri_glasspane::PaneSupervisor::new();
let pane_id = "pi-spawn-proof";
supervisor.attach_pane_at(pane_id, "fake-pi", SystemTime::now());
supervisor.attach_pane_at(pane_id, "sample-pi", SystemTime::now());
let mut child = Command::new(PathBuf::from("python3"))
.arg(&script)
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("failed to spawn fake-pi-agent.py");
.expect("failed to spawn sample-pi-agent.py");
let stdout = child.stdout.take().expect("no stdout");
let mut reader = BufReader::new(stdout).lines();
@ -60,7 +63,7 @@ async fn pi_spawn_path_produces_correct_glasspane_state() {
}
let status = child.wait().await.expect("child wait failed");
assert!(status.success(), "fake-pi-agent.py exited with {status}");
assert!(status.success(), "sample-pi-agent.py exited with {status}");
assert!(
accepted >= 5,
"expected >=5 accepted JSONL lines, got {accepted}"

View file

@ -9,7 +9,7 @@
//! Phase 3 starts the supervision layer: Colibri-owned pane ids are distinct
//! from Pi session ids, ingestion is streaming, `last_event_at` is kept as real
//! time internally, and `stalled` is derived from event silence. Live PTY launch
//! is scaffolded with `portable-pty`; tests use fake JSONL readers until the
//! is scaffolded with `portable-pty`; tests use sample JSONL readers until the
//! FreeBSD validation lane exercises real terminals.
use std::{
@ -240,7 +240,7 @@ pub struct PaneReaderStats {
/// Stateful streaming ingestor for Pi `--mode json` JSONL.
///
/// This is intentionally independent from PTY/process ownership: tests can feed
/// fake readers, while live panes can wire PTY stdout to the same API later.
/// sample readers, while live panes can wire PTY stdout to the same API later.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PiJsonlIngestor {
state: AgentState,
@ -465,7 +465,7 @@ impl PaneSupervisor {
self.get_mut(pane_id)?.ingest_line_at(line, observed_at)
}
/// Test-first Phase-3 path: feed a fake/recorded JSONL reader through the
/// Test-first Phase-3 path: feed a sample/recorded JSONL reader through the
/// same streaming API that a PTY stdout reader will use.
pub fn ingest_jsonl_reader_at<R: BufRead>(
&mut self,
@ -505,7 +505,7 @@ impl PaneSupervisor {
/// Drain a JSONL reader into an already-attached pane using wall-clock time.
///
/// This is the Phase-3.1 bridge between fake JSONL readers and real PTY stdout:
/// This is the Phase-3.1 bridge between sample JSONL readers and real PTY stdout:
/// both paths feed the same streaming ingestor and update the same supervisor.
pub fn run_pane_reader<R: BufRead>(
supervisor: &mut PaneSupervisor,
@ -550,7 +550,7 @@ where
}
/// Minimal portable-pty launch spec. Live FreeBSD validation is intentionally
/// later; this keeps the process/PTY seam explicit while tests use fake readers.
/// later; this keeps the process/PTY seam explicit while tests use sample readers.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PtyLaunchSpec {
pub program: String,
@ -578,7 +578,7 @@ pub struct PtyPane {
}
/// Spawn a command under a native PTY. The caller owns wiring `reader` into
/// [`PiJsonlIngestor`] / [`PaneSupervisor`]. Not exercised by CI yet; fake JSONL
/// [`PiJsonlIngestor`] / [`PaneSupervisor`]. Not exercised by CI yet; sample JSONL
/// readers cover the supervision state model first.
pub fn spawn_pty_command(
spec: &PtyLaunchSpec,
@ -606,7 +606,7 @@ pub fn spawn_pty_command(
/// Drain the reader side of a live PTY-backed pane into the supervisor.
///
/// The caller owns spawning/waiting/killing the child; this function only folds
/// stdout JSONL into Glasspane state. CI still covers this through fake readers.
/// stdout JSONL into Glasspane state. CI still covers this through sample readers.
pub fn run_pty_pane_reader(
supervisor: &mut PaneSupervisor,
pane_id: &str,
@ -851,7 +851,7 @@ mod tests {
}
#[test]
fn supervisor_fake_jsonl_reader_uses_streaming_api() {
fn supervisor_sample_jsonl_reader_uses_streaming_api() {
let jsonl = [
r#"{"type":"session","id":"pi-s","cwd":"/repo"}"#,
"not json",

View file

@ -99,7 +99,7 @@ colibri snapshot
Expected: valid JSON snapshot. It may have no panes before an agent is spawned.
Optional local fake-agent check if `colibri-test-agent` is included:
Optional local sample-agent check if `colibri-test-agent` is included:
```sh
colibri spawn-local /usr/local/bin/colibri-test-agent --session-id iso-check

View file

@ -15,7 +15,7 @@
```
/usr/local/bin/colibri-daemon ← daemon binary (foreground, no self-daemonize)
/usr/local/bin/colibri ← CLI client (status, create-task, etc.)
/usr/local/bin/colibri-test-agent ← test agent (fake Pi JSONL)
/usr/local/bin/colibri-test-agent ← test agent (sample Pi JSONL)
/usr/local/etc/rc.d/colibri_daemon ← rc.d service script
/usr/local/etc/colibri/
rc.conf.sample ← service config template

View file

@ -129,14 +129,14 @@ to state transitions before it can be validated.
### What exists
| Capability | Location | Status |
| ------------------- | ------------------------------------------ | ---------------------------------------------------------- |
| `Spawner::spawn()` | `crates/colibri-daemon/src/spawner.rs:585` | done — provider routing, jail wrap, retry/backoff |
| `AgentHandle` | `crates/colibri-daemon/src/spawner.rs:465` | done — tracks child, stdout for glasspane, kill, poll_exit |
| `take_stdout()` | `crates/colibri-daemon/src/spawner.rs:500` | done — hands stdout to glasspane supervision |
| Jail confinement | `crates/colibri-daemon/src/spawner.rs:332` | done — named/ephemeral, staged env payload, priv modes |
| `fake-pi-agent.py` | `scripts/fake-pi-agent.py` | exists — emits JSONL events for testing |
| Glasspane ingestion | `crates/colibri-glasspane/` | done — ingests JSONL, tracks pane state |
| Capability | Location | Status |
| -------------------- | ------------------------------------------ | ---------------------------------------------------------- |
| `Spawner::spawn()` | `crates/colibri-daemon/src/spawner.rs:585` | done — provider routing, jail wrap, retry/backoff |
| `AgentHandle` | `crates/colibri-daemon/src/spawner.rs:465` | done — tracks child, stdout for glasspane, kill, poll_exit |
| `take_stdout()` | `crates/colibri-daemon/src/spawner.rs:500` | done — hands stdout to glasspane supervision |
| Jail confinement | `crates/colibri-daemon/src/spawner.rs:332` | done — named/ephemeral, staged env payload, priv modes |
| `sample-pi-agent.py` | `scripts/sample-pi-agent.py` | exists — emits JSONL events for testing |
| Glasspane ingestion | `crates/colibri-glasspane/` | done — ingests JSONL, tracks pane state |
### What's missing
@ -150,7 +150,7 @@ to state transitions before it can be validated.
- Hand stdout to glasspane
2. **End-to-end integration test.**
Using `scripts/fake-pi-agent.py` (or a Rust mock binary):
Using `scripts/sample-pi-agent.py` (or a Rust mock binary):
- Start daemon
- Create a task + intake it
- Wait for scheduler tick + spawn
@ -178,13 +178,13 @@ to state transitions before it can be validated.
- `crates/colibri-daemon/src/daemon.rs:242``session_rotation()` (working, good reference for how other background loops iterate state)
- `crates/colibri-daemon/src/spawner.rs:585``Spawner::spawn()` (working)
- `crates/colibri-daemon/src/socket.rs` — socket command dispatch (check for spawn commands)
- `scripts/fake-pi-agent.py` — test agent that emits JSONL
- `scripts/sample-pi-agent.py` — test agent that emits JSONL
- `crates/colibri-glasspane/src/` — JSONL ingestion + pane state machine
### Suggested owner
Rust lane (Hermes on Linux). Can implement and test fully on Linux with
`fake-pi-agent.py`. FreeBSD validation confirms jail path works.
`sample-pi-agent.py`. FreeBSD validation confirms jail path works.
---
@ -303,11 +303,11 @@ wiring, no platform-specific behavior.
## Summary table
| # | Item | Blocks | Linux-doable | Effort |
| --- | --------------------------- | ------------------- | --------------------------- | ------ |
| 1 | ISO boot/runtime validation | Gate 1 | no (needs FreeBSD boot) | small |
| 2 | Pi spawn end-to-end | Gate 2 | yes (with fake-pi-agent.py) | medium |
| 3 | Cost mode enforcement | core design promise | yes (pure logic) | medium |
| # | Item | Blocks | Linux-doable | Effort |
| --- | --------------------------- | ------------------- | ----------------------------- | ------ |
| 1 | ISO boot/runtime validation | Gate 1 | no (needs FreeBSD boot) | small |
| 2 | Pi spawn end-to-end | Gate 2 | yes (with sample-pi-agent.py) | medium |
| 3 | Cost mode enforcement | core design promise | yes (pure logic) | medium |
All three are medium effort and can be worked in parallel. None require
FreeBSD to implement — only to validate the final result.

View file

@ -1,5 +1,5 @@
#!/usr/bin/env python3
"""Fake Pi agent — emits JSONL in the colibri-pi-events format.
"""Sample Pi agent — emits JSONL in the colibri-pi-events format.
Used by Colibri integration tests to validate the spawn JSONL glasspane path.
"""
import sys