use std::path::Path;
use std::process::Command;
use io_harness::{
approve::ApproveAll, policy::Policy, run_with, OpenRouter, Provider, Store, TaskContract,
Verification,
};
const COLOUR: [u8; 3] = [255, 0, 255];
const COLOUR_NAME: &str = "magenta";
fn git(dir: &Path, args: &[&str]) -> String {
let out = Command::new("git")
.args(args)
.current_dir(dir)
.env("GIT_CONFIG_NOSYSTEM", "1")
.env("GIT_AUTHOR_NAME", "Fixture")
.env("GIT_AUTHOR_EMAIL", "fixture@example.invalid")
.env("GIT_COMMITTER_NAME", "Fixture")
.env("GIT_COMMITTER_EMAIL", "fixture@example.invalid")
.output()
.expect("git");
String::from_utf8_lossy(&out.stdout).trim().to_string()
}
#[tokio::main]
async fn main() -> io_harness::Result<()> {
let dir = tempfile::tempdir().unwrap();
let root = dir.path();
let img = image::RgbImage::from_pixel(64, 64, image::Rgb(COLOUR));
img.save(root.join("swatch.png")).unwrap();
std::fs::write(root.join("README.md"), "the swatch is in swatch.png\n").unwrap();
git(root, &["init", "--initial-branch=main"]);
git(root, &["add", "README.md", "swatch.png"]);
git(root, &["commit", "-m", "fixture"]);
let before = git(root, &["rev-parse", "HEAD"]);
let contract = TaskContract::workspace(
"Look at swatch.png. Write the name of the colour you see, as a single \
lowercase word, into COLOUR.md. Then stage COLOUR.md and commit it, with \
a message describing what you did. Only once the commit exists, write \
DONE.md containing the single word: committed",
root,
)
.with_verification(Verification::WorkspaceFileContains {
file: "DONE.md".into(),
needle: "committed".into(),
})
.with_max_steps(12)
.with_commit_identity("io-harness agent", "agent@io-harness.invalid");
let store_dir = tempfile::tempdir().unwrap();
let store = Store::open(store_dir.path().join("trace.db"))?;
let provider = OpenRouter::from_env()?;
println!("--- running against {} ---", provider.name());
let result = run_with(
&contract,
&provider,
&store,
&Policy::permissive(),
&ApproveAll,
)
.await?;
println!("outcome: {:?}", result.outcome);
let wrote = std::fs::read_to_string(root.join("COLOUR.md")).unwrap_or_default();
let saw_it = wrote.to_lowercase().contains(COLOUR_NAME);
println!(
"\nimage: COLOUR.md = {:?} -> {}",
wrote.trim(),
if saw_it {
"NAMED THE COLOUR — the bytes reached the model"
} else {
"did NOT name the colour"
}
);
let after = git(root, &["rev-parse", "HEAD"]);
let committed = after != before;
let files = git(root, &["show", "--name-only", "--format=", "HEAD"]);
let status = git(root, &["status", "--porcelain"]);
println!(
"git: HEAD {} -> {}\n new commit: {}\n message: {:?}\n files in it: {:?}\n working tree: {}",
&before[..7.min(before.len())],
&after[..7.min(after.len())],
committed,
git(root, &["log", "-1", "--format=%s"]),
files,
if status.is_empty() {
"clean".to_string()
} else {
format!("still dirty:\n{status}")
}
);
println!(
" author: {}",
git(root, &["log", "-1", "--format=%an <%ae>"])
);
let ok = saw_it && committed && files.contains("COLOUR.md");
println!(
"\n=== {} ===",
if ok {
"BOTH HALVES PROVEN: the model saw the image, and the run ended as a commit"
} else {
"NOT PROVEN — see above for which half failed"
}
);
Ok(())
}