use std::fs;
use std::path::{Path, PathBuf};
use crate::adapters::{CliDispatchContext, CliJudgeContext, adapter_for};
use crate::core::{AvailableSkill, Harness, Mode};
use super::dispatch::{DispatchTaskOpts, ManifestContext, build_dispatch_task, build_manifest};
use super::runbook::{RunbookContext, build_runbook};
fn golden_path(rel: &str) -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR"))
.join("tests/golden")
.join(rel)
}
fn assert_golden(rel: &str, actual: &str) {
let path = golden_path(rel);
if std::env::var_os("GOLDEN_BLESS").is_some_and(|v| v == "1") {
fs::create_dir_all(path.parent().unwrap()).unwrap();
fs::write(&path, actual).unwrap();
}
let expected = fs::read_to_string(&path).unwrap_or_else(|_| {
panic!(
"golden fixture {} is missing — run `GOLDEN_BLESS=1 cargo test golden_` and commit the fixtures",
path.display()
)
});
assert_eq!(
actual, expected,
"output diverged from tests/golden/{rel}; if the change is intentional, \
re-bless with `GOLDEN_BLESS=1 cargo test golden_` and review the fixture diff"
);
}
fn fixed_skills() -> Vec<AvailableSkill> {
vec![
AvailableSkill {
name: "widget-skill".to_string(),
path: "/work/staged/widget-skill/SKILL.md".to_string(),
description: "Builds widgets the house way.".to_string(),
},
AvailableSkill {
name: "aux-helper".to_string(),
path: "/work/staged/aux-helper/SKILL.md".to_string(),
description: "Assists with auxiliary chores.".to_string(),
},
]
}
fn staged_task(harness: Harness) -> DispatchTaskOpts<'static> {
DispatchTaskOpts {
eval_id: "demo-eval",
condition: "with_skill",
staged_skill_path: Some("/work/staged/widget-skill/SKILL.md"),
user_prompt: "Build me a widget.",
fixtures: vec!["/work/fixtures/input.txt".to_string()],
outputs_dir: "/work/outputs",
cond_dir: "/work/cond",
bootstrap_content: Some("Session guidelines: be concise."),
plan_mode_content: Some("PLAN STEP"),
skill_name: "widget-skill",
available_skills: fixed_skills(),
harness,
..Default::default()
}
}
fn bare_task(harness: Harness) -> DispatchTaskOpts<'static> {
DispatchTaskOpts {
eval_id: "demo-eval",
condition: "without_skill",
user_prompt: "Build me a widget.",
fixtures: vec!["/work/fixtures/input.txt".to_string()],
outputs_dir: "/work/outputs-b",
cond_dir: "/work/cond-b",
skill_name: "widget-skill",
harness,
..Default::default()
}
}
fn render_manifest(harness: Harness, guard: bool, agent_model: Option<&str>) -> String {
let slug =
adapter_for(harness).staged_slug("slow-powers-eval-", 2, "with_skill", "widget-skill");
let mut staged = staged_task(harness);
staged.staged_skill_slug = Some(&slug);
let tasks = vec![
build_dispatch_task(&staged).unwrap(),
build_dispatch_task(&bare_task(harness)).unwrap(),
];
build_manifest(
"widget-skill",
Mode::Revision,
Some("iteration-1"),
2,
"2026-01-01T00:00:00Z",
&tasks,
ManifestContext {
harness,
guard,
agent_model,
},
)
}
#[test]
fn golden_runbook_per_harness() {
for harness in Harness::known() {
let label = adapter_for(harness).label();
let dir = PathBuf::from("/work/.eval-magic/widget-skill/iteration-2");
let book = build_runbook(&RunbookContext {
harness,
skill_name: "widget-skill",
iteration: 2,
iteration_dir: &dir,
mode: Mode::Revision,
cond_a: "old_skill",
cond_b: "new_skill",
num_tasks: 6,
target_args: " --skill-dir /tmp/skills --skill widget-skill",
guard: true,
agent_model: Some("model-x"),
});
assert_golden(&format!("{label}/runbook.golden.md"), &book);
}
}
#[test]
fn golden_manifest_per_harness() {
for harness in Harness::known() {
let label = adapter_for(harness).label();
let manifest = render_manifest(harness, true, Some("model-x"));
assert_golden(&format!("{label}/manifest.golden.md"), &manifest);
}
}
#[test]
fn golden_manifest_codex_without_guard() {
let manifest = render_manifest(Harness::resolve("codex").unwrap(), false, Some("model-x"));
assert_golden("codex/manifest-noguard.golden.md", &manifest);
}
#[test]
fn golden_manifest_claude_without_model() {
let manifest = render_manifest(Harness::resolve("claude-code").unwrap(), true, None);
assert_golden("claude-code/manifest-nomodel.golden.md", &manifest);
}
#[test]
fn golden_dispatch_prompt_per_harness() {
for harness in Harness::known() {
let label = adapter_for(harness).label();
let slug =
adapter_for(harness).staged_slug("slow-powers-eval-", 2, "with_skill", "widget-skill");
let mut opts = staged_task(harness);
opts.staged_skill_slug = Some(&slug);
let task = build_dispatch_task(&opts).unwrap();
assert_golden(
&format!("{label}/dispatch-prompt.golden.txt"),
&task.dispatch_prompt,
);
}
}
#[test]
fn golden_skills_block_per_harness() {
for harness in Harness::known() {
let label = adapter_for(harness).label();
let block = adapter_for(harness).render_available_skills_block(&fixed_skills());
assert_golden(&format!("{label}/skills-block.golden.txt"), &block);
}
}
#[test]
fn golden_judge_recipe_claude_and_codex() {
for (harness, guard, rel) in [
(
Harness::resolve("claude-code").unwrap(),
true,
"claude-code/judge-recipe.golden.md",
),
(
Harness::resolve("codex").unwrap(),
true,
"codex/judge-recipe.golden.md",
),
(
Harness::resolve("codex").unwrap(),
false,
"codex/judge-recipe-noguard.golden.md",
),
] {
let recipe = adapter_for(harness)
.cli_judge_next_steps(CliJudgeContext {
guard,
iteration_dir: Path::new("/work/iter-1"),
})
.expect("judge recipe is wired for this harness");
assert_golden(rel, &recipe);
}
}
#[test]
fn golden_opencode_next_steps_with_and_without_model() {
for (agent_model, rel) in [
(Some("model-x"), "opencode/next-steps-model.golden.txt"),
(None, "opencode/next-steps-nomodel.golden.txt"),
] {
let steps =
adapter_for(Harness::resolve("opencode").unwrap()).cli_next_steps(CliDispatchContext {
guard: false,
target_args: " --skill-dir /tmp/skills --skill widget-skill",
iteration: 2,
agent_model,
});
assert_golden(rel, &steps);
}
}