test: rename 'fake' test agent → 'sample' #159
10 changed files with 60 additions and 52 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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}"
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
Loading…
Add table
Reference in a new issue