use shepherd_compiler::{
CompileInput, HarnessProfile, Portability, RoleInput, SkillInput, SkillResource, TargetHarness,
compile,
};
const CANONICAL_ROLE_STARTUP_SKILLS: [(&str, &str); 9] = [
("auditor", "reviewing"),
("coder", "implementing"),
("conductor", "lane-execution"),
("critic", "reviewing"),
("discovery", "researching"),
("engineer", "planning"),
("planter", "planting"),
("shepherd", "shepherd"),
("worker", "artifact-work"),
];
fn role(name: &str, write_eligible: bool, model_hint: &str) -> RoleInput {
RoleInput {
role: name.into(),
description: format!("Use when acting as the {name} role."),
model_hint: model_hint.into(),
write_eligible,
dispatchable: name != "shepherd",
capabilities: vec!["read".into(), "search".into()],
startup_skill: "context".into(),
write_scope: if write_eligible {
"assigned scope"
} else {
"none"
}
.into(),
body: format!("# {name}\n\nDo the assigned work.\n"),
source_path: format!("content/roles/{name}.md"),
source_content: format!("role: {name}\n"),
}
}
fn skill(name: &str, portability: Portability) -> SkillInput {
SkillInput {
name: name.into(),
description: format!("Use when {name} behavior is required."),
portability,
resources: Vec::new(),
body: format!("# {name}\n\nFollow the contract.\n"),
source_path: format!("content/skills/{name}/SKILL.md"),
source_content: format!("name: {name}\n"),
}
}
fn input() -> CompileInput {
CompileInput {
roles: vec![
role("auditor", false, "standard"),
role("coder", true, "standard"),
role("shepherd", true, "inherit-caller"),
],
skills: vec![
skill("context", Portability::CrossHarness),
skill("harness", Portability::ClaudeOnly),
],
}
}
fn planter_input() -> CompileInput {
let mut planter = role("planter", true, "reasoning-high");
planter.dispatchable = false;
planter.capabilities = vec![
"read".into(),
"search".into(),
"shell".into(),
"write".into(),
"skill-load".into(),
"tool-discovery".into(),
"ask-operator".into(),
];
planter.startup_skill = "planting".into();
planter.write_scope =
"the bound run's mesh.md and seed.md only; native verification and seed-pointer commands"
.into();
CompileInput {
roles: vec![planter],
skills: vec![skill("planting", Portability::CrossHarness)],
}
}
#[test]
fn planter_contract_mutations_fail_before_emission() {
let mut capability = planter_input();
capability.roles[0].capabilities.push("dispatch".into());
let error = compile(&capability, &HarnessProfile::pi()).expect_err("capability");
assert!(error.to_string().contains("planter contract"));
let mut dispatchable = planter_input();
dispatchable.roles[0].dispatchable = true;
let error = compile(&dispatchable, &HarnessProfile::pi()).expect_err("dispatchable");
assert!(error.to_string().contains("planter contract"));
let mut write_scope = planter_input();
write_scope.roles[0].write_scope = "the run/plan namespace".into();
let error = compile(&write_scope, &HarnessProfile::pi()).expect_err("write scope");
assert!(error.to_string().contains("planter contract"));
}
#[test]
fn embedded_engineer_has_the_required_planning_attachment_and_digest() {
let input = shepherd_compiler::content::embedded_compile_input().expect("embedded content");
let engineer = input
.roles
.iter()
.find(|role| role.role == "engineer")
.expect("canonical Engineer");
assert_eq!(engineer.startup_skill, "planning");
for profile in HarnessProfile::canonical() {
let tree = compile(&input, &profile).expect("canonical target compile");
let emitted = tree
.roles
.iter()
.find(|role| role.role == "engineer")
.expect("emitted Engineer");
assert_eq!(emitted.startup_skill.as_deref(), Some("planning"));
assert_eq!(
emitted.startup_skill_sha256.as_deref().map(str::len),
Some(64)
);
}
}
#[test]
fn canonical_role_startup_skills_are_exact_and_fail_closed() {
let canonical =
shepherd_compiler::content::embedded_compile_input().expect("embedded canonical content");
for (role_name, expected_skill) in CANONICAL_ROLE_STARTUP_SKILLS {
let role = canonical
.roles
.iter()
.find(|role| role.role == role_name)
.unwrap_or_else(|| panic!("canonical role {role_name}"));
assert_eq!(
role.startup_skill, expected_skill,
"canonical startup skill for {role_name}"
);
let mut empty = canonical.clone();
empty
.roles
.iter_mut()
.find(|role| role.role == role_name)
.expect("role for empty mutation")
.startup_skill = String::new();
let error = match compile(&empty, &HarnessProfile::claude()) {
Ok(_) => panic!("{role_name} accepted an empty startup skill"),
Err(error) => error,
};
assert!(
error.to_string().contains("skill"),
"{role_name} empty startup skill: {error}"
);
let mut misspelled = canonical.clone();
misspelled
.roles
.iter_mut()
.find(|role| role.role == role_name)
.expect("role for misspelled mutation")
.startup_skill = format!("{expected_skill}-typo");
let error = match compile(&misspelled, &HarnessProfile::claude()) {
Ok(_) => panic!("{role_name} accepted a misspelled startup skill"),
Err(error) => error,
};
assert!(
error.to_string().contains("skill"),
"{role_name} misspelled startup skill: {error}"
);
let mut incompatible = canonical.clone();
incompatible.roles.retain(|role| role.role == role_name);
incompatible
.skills
.iter_mut()
.find(|skill| skill.name == expected_skill)
.expect("canonical startup bundle")
.portability = Portability::ClaudeOnly;
let error = match compile(&incompatible, &HarnessProfile::pi()) {
Ok(_) => panic!("{role_name} accepted a target-incompatible skill"),
Err(error) => error,
};
assert!(
error.to_string().contains("not portable to target `pi`"),
"{role_name} target-incompatible startup skill: {error}"
);
}
}
#[test]
fn canonical_role_dispatch_topology_is_exact_and_fail_closed() {
let canonical =
shepherd_compiler::content::embedded_compile_input().expect("embedded canonical content");
for role_name in ["shepherd", "planter"] {
let role = canonical
.roles
.iter()
.find(|role| role.role == role_name)
.unwrap_or_else(|| panic!("canonical role {role_name}"));
assert!(!role.dispatchable, "{role_name} must remain a root");
let mut mutated = canonical.clone();
mutated
.roles
.iter_mut()
.find(|role| role.role == role_name)
.expect("root role mutation")
.dispatchable = true;
let error = compile(&mutated, &HarnessProfile::claude())
.expect_err("canonical root accepted child dispatchability");
let expected_contract = if role_name == "planter" {
"planter contract"
} else {
"canonical role topology"
};
assert!(
error.to_string().contains(expected_contract),
"{role_name} root mutation: {error}"
);
}
for role_name in [
"auditor",
"coder",
"conductor",
"critic",
"discovery",
"engineer",
"worker",
] {
let role = canonical
.roles
.iter()
.find(|role| role.role == role_name)
.unwrap_or_else(|| panic!("canonical role {role_name}"));
assert!(role.dispatchable, "{role_name} must remain a child");
let mut mutated = canonical.clone();
mutated
.roles
.iter_mut()
.find(|role| role.role == role_name)
.expect("child role mutation")
.dispatchable = false;
let error = compile(&mutated, &HarnessProfile::claude())
.expect_err("canonical child accepted root dispatchability");
assert!(
error.to_string().contains("canonical role topology"),
"{role_name} child mutation: {error}"
);
}
}
#[test]
fn all_targets_emit_sorted_provenanced_bounded_trees_reproducibly() {
for profile in HarnessProfile::canonical() {
let first = compile(&input(), &profile).expect("compile target");
let second = compile(&input(), &profile).expect("compile target again");
assert_eq!(first, second);
assert_eq!(first.digest.len(), 64);
assert!(
first
.files
.windows(2)
.all(|pair| pair[0].path < pair[1].path)
);
assert!(first.files.iter().all(|file| {
file.source_sha256.len() == 64
&& file.content_sha256.len() == 64
&& file.measurement.utf8_bytes == file.content.len()
&& file.mode == 0o644
}));
}
}
#[test]
fn target_surfaces_and_write_eligibility_are_exact() {
let claude = compile(&input(), &HarnessProfile::claude()).expect("Claude");
assert!(
claude
.files
.iter()
.any(|file| file.path == "agents/shepherd.md")
);
assert!(
claude
.files
.iter()
.any(|file| file.path == "skills/harness/SKILL.md")
);
let codex = compile(&input(), &HarnessProfile::codex()).expect("Codex");
assert!(codex.files.iter().all(|file| {
!file.path.starts_with("agents/")
&& !file.path.starts_with("prompts/")
&& !file.path.starts_with("commands/")
}));
let config = codex
.files
.iter()
.find(|file| file.path == "shepherd.codex.toml")
.expect("Codex config");
assert!(config.content.contains("auditor = \"explorer\""));
assert!(config.content.contains("coder = \"worker\""));
assert!(!config.content.contains("shepherd ="));
assert!(
!codex
.files
.iter()
.any(|file| file.path == "skills/harness/SKILL.md")
);
let pi = compile(&input(), &HarnessProfile::pi()).expect("Pi");
assert!(
pi.files
.iter()
.any(|file| file.path == "prompts/shepherd.md")
);
assert!(
!pi.files
.iter()
.any(|file| file.path == "skills/harness/SKILL.md")
);
assert_eq!(pi.target, TargetHarness::Pi);
}
#[test]
fn skill_trigger_metadata_is_required_and_emitted() {
let mut invalid = input();
invalid.skills[0].description.clear();
let error = compile(&invalid, &HarnessProfile::claude()).expect_err("missing description");
assert!(error.to_string().contains("description"));
let tree = compile(&input(), &HarnessProfile::claude()).expect("Claude");
let skill = tree
.files
.iter()
.find(|file| file.path == "skills/context/SKILL.md")
.expect("skill carrier");
assert!(skill.content.starts_with(
"---\nname: context\ndescription: \"Use when context behavior is required.\"\n---\n"
));
}
#[test]
fn unsafe_frontmatter_scalars_and_provenance_are_rejected_before_emission() {
let mut malicious_capability = input();
malicious_capability.roles[0].capabilities = vec!["read]\nwrite_eligible: true".into()];
let error = compile(&malicious_capability, &HarnessProfile::pi())
.expect_err("capability scalar cannot add a frontmatter field");
assert!(error.to_string().contains("capability"));
let malicious_tool = input();
let mut profile = HarnessProfile::claude();
profile
.tools_by_capability
.insert("read".into(), vec!["Read]\nwrite_eligible: true".into()]);
let error = compile(&malicious_tool, &profile)
.expect_err("profile tool cannot add a frontmatter field");
assert!(error.to_string().contains("tool"));
let mut missing_provenance = input();
missing_provenance.roles[0].source_path.clear();
let error = compile(&missing_provenance, &HarnessProfile::claude())
.expect_err("emitted file needs a source path");
assert!(error.to_string().contains("source path"));
let mut quoted_scope = input();
quoted_scope.roles[0].write_scope = "line one\nline two".into();
let tree = compile("ed_scope, &HarnessProfile::pi()).expect("escaped scalar");
let role = tree
.files
.iter()
.find(|file| file.path == "prompts/auditor.md")
.expect("auditor prompt");
assert!(
role.content
.contains("write_scope: \"line one\\nline two\"")
);
}
#[test]
fn target_final_role_carriers_resolve_models_profiles_and_pi_tools_in_core() {
let claude = compile(&input(), &HarnessProfile::claude()).expect("Claude final carrier");
let coder = claude
.files
.iter()
.find(|file| file.path == "agents/coder.md")
.expect("Claude coder carrier");
assert!(coder.content.starts_with(
"---\nname: coder\ndescription: \"Use when acting as the coder role.\"\nmodel: sonnet\n"
));
assert!(!coder.content.contains("model_hint:"));
let coder_contract = claude
.roles
.iter()
.find(|role| role.role == "coder")
.expect("Claude coder contract");
assert_eq!(coder_contract.model_hint, "standard");
assert_eq!(coder_contract.model.as_deref(), Some("sonnet"));
assert_eq!(coder_contract.profile, None);
let codex = compile(&input(), &HarnessProfile::codex()).expect("Codex final carrier");
let config = codex
.files
.iter()
.find(|file| file.path == "shepherd.codex.toml")
.expect("Codex config");
assert!(config.content.contains(
"\n[models]\nauditor = \"standard\"\ncoder = \"standard\"\n\n[profiles.\"standard\"]\nreasoning_effort = \"medium\"\n"
));
assert!(!config.content.contains("shepherd = \"inherit-caller\""));
let coder_contract = codex
.roles
.iter()
.find(|role| role.role == "coder")
.expect("Codex coder contract");
assert_eq!(coder_contract.model_hint, "standard");
assert_eq!(coder_contract.model.as_deref(), Some("gpt-5.6-luna"));
assert_eq!(coder_contract.profile.as_deref(), Some("standard"));
assert_eq!(coder_contract.reasoning_effort.as_deref(), Some("medium"));
let mut pi_input = input();
pi_input.roles[1].capabilities.push("dispatch".into());
let pi = compile(&pi_input, &HarnessProfile::pi()).expect("Pi final carrier");
let coder = pi
.files
.iter()
.find(|file| file.path == "prompts/coder.md")
.expect("Pi coder carrier");
assert!(!coder.content.contains("\nmodel:"));
assert!(!coder.content.contains("\ntools:"));
let coder_contract = pi
.roles
.iter()
.find(|role| role.role == "coder")
.expect("Pi coder contract");
assert_eq!(coder_contract.model_hint, "standard");
assert_eq!(coder_contract.model, None);
assert_eq!(coder_contract.tools, ["read", "grep", "find", "subagent"]);
assert!(coder_contract.unsupported_capabilities.is_empty());
let root_contract = pi
.roles
.iter()
.find(|role| role.role == "shepherd")
.expect("Pi root contract");
assert_eq!(root_contract.model_hint, "inherit-caller");
assert_eq!(root_contract.model.as_deref(), Some("inherit"));
}
#[test]
fn economy_hint_resolves_and_matches_target_shape_on_all_canonical_profiles() {
for target_profile in HarnessProfile::canonical() {
let mut economy_input = input();
for role in &mut economy_input.roles {
if role.role != "shepherd" {
role.model_hint = "economy".into();
}
}
let tree = compile(&economy_input, &target_profile).unwrap_or_else(|error| {
panic!(
"{} economy hint should resolve: {error}",
target_profile.target.as_str()
)
});
let coder = tree
.roles
.iter()
.find(|role| role.role == "coder")
.expect("coder resolved for economy hint");
match target_profile.target {
TargetHarness::Claude => {
assert_eq!(coder.model.as_deref(), Some("haiku"));
assert_eq!(coder.profile, None);
assert_eq!(coder.reasoning_effort, None);
}
TargetHarness::Codex => {
assert_eq!(coder.model.as_deref(), Some("gpt-5.6-luna"));
assert_eq!(coder.profile.as_deref(), Some("economy"));
assert_eq!(coder.reasoning_effort.as_deref(), Some("low"));
}
TargetHarness::Pi => {
assert_eq!(coder.model, None);
assert_eq!(coder.profile, None);
assert_eq!(coder.reasoning_effort, None);
}
_ => panic!("NOTHING HERE"),
}
}
}
#[test]
fn missing_or_malformed_target_model_mapping_fails_closed() {
let mut missing = HarnessProfile::claude();
missing.model_by_hint.remove("standard");
let error = compile(&input(), &missing).expect_err("missing model mapping");
assert!(error.to_string().contains("model mapping"));
let mut malformed = HarnessProfile::codex();
malformed
.model_by_hint
.get_mut("standard")
.expect("canonical mapping")
.reasoning_effort = None;
let error = compile(&input(), &malformed).expect_err("incomplete Codex profile");
assert!(error.to_string().contains("reasoning effort"));
}
fn resource(path: &str, content: &[u8], executable: bool) -> SkillResource {
SkillResource {
relative_path: path.into(),
content: content.into(),
executable,
source_path: format!("content/skills/context/{path}"),
}
}
#[test]
fn startup_skill_resolves_to_the_complete_emitted_bundle() {
let mut subject = input();
subject.roles[1].startup_skill = "context".into();
subject.skills[0].resources = vec![
resource("references/contract.md", b"contract\n", false),
resource("scripts/check.sh", b"#!/bin/sh\nexit 0\n", true),
];
let first = compile(&subject, &HarnessProfile::claude()).expect("startup skill");
let coder = first
.roles
.iter()
.find(|role| role.role == "coder")
.expect("coder contract");
assert_eq!(coder.startup_skill.as_deref(), Some("context"));
assert_eq!(
coder.startup_skill_sha256.as_deref().map(str::len),
Some(64)
);
assert_eq!(
first
.files
.iter()
.find(|file| file.path == "skills/context/references/contract.md")
.expect("reference")
.mode,
0o644
);
assert_eq!(
first
.files
.iter()
.find(|file| file.path == "skills/context/scripts/check.sh")
.expect("script")
.mode,
0o755
);
subject.skills[0].resources[0].content = b"changed contract\n".to_vec();
let changed = compile(&subject, &HarnessProfile::claude()).expect("changed resource");
let changed_coder = changed
.roles
.iter()
.find(|role| role.role == "coder")
.expect("changed coder contract");
assert_ne!(first.digest, changed.digest);
assert_ne!(
coder.startup_skill_sha256,
changed_coder.startup_skill_sha256
);
}
#[test]
fn startup_skill_emits_exact_target_native_role_attachments() {
let mut subject = input();
let mut example = role("example", true, "standard");
example.startup_skill = "example".into();
subject.roles.push(example);
subject
.skills
.push(skill("example", Portability::CrossHarness));
let claude = compile(&subject, &HarnessProfile::claude()).expect("Claude attachment");
let claude_agent = claude
.files
.iter()
.find(|file| file.path == "agents/example.md")
.expect("Claude example agent");
assert_eq!(
claude_agent.content,
"---\nname: example\ndescription: \"Use when acting as the example role.\"\nmodel: sonnet\ntools: [Read, NotebookRead, Glob, Grep]\nskills: [example]\ndispatchable: true\nwrite_eligible: true\nwrite_scope: \"assigned scope\"\n---\n\n# example\n\nDo the assigned work.\n"
);
let codex = compile(&subject, &HarnessProfile::codex()).expect("Codex attachment");
let codex_agent = codex
.files
.iter()
.find(|file| file.path == ".codex/agents/example.toml")
.expect("Codex example agent");
assert_eq!(
codex_agent.content,
"name = \"example\"\ndescription = \"Use when acting as the example role.\"\nmodel = \"gpt-5.6-luna\"\nmodel_reasoning_effort = \"medium\"\nsandbox_mode = \"workspace-write\"\ndeveloper_instructions = \"First invoke the installed `$example` skill. Do not continue until it loads successfully.\\n\\n# example\\n\\nDo the assigned work.\"\n"
);
assert!(
codex
.files
.iter()
.any(|file| file.path == ".agents/skills/example/SKILL.md")
);
assert!(
!codex
.files
.iter()
.any(|file| file.path == ".codex/agents/shepherd.toml")
);
let root = codex
.roles
.iter()
.find(|role| role.role == "shepherd")
.expect("Codex root contract");
assert!(!root.dispatchable);
assert_eq!(root.carrier_path, "shepherd.codex.toml");
let pi = compile(&subject, &HarnessProfile::pi()).expect("Pi attachment");
let pi_prompt = pi
.files
.iter()
.find(|file| file.path == "prompts/example.md")
.expect("Pi example prompt");
assert_eq!(
pi_prompt.content,
"---\nname: example\ndescription: \"Use when acting as the example role.\"\ncapabilities: [read, search]\nskills: example\ndispatchable: true\nwrite_eligible: true\nwrite_scope: \"assigned scope\"\n---\n\n# example\n\nDo the assigned work.\n"
);
for tree in [&claude, &codex, &pi] {
let contract = tree
.roles
.iter()
.find(|role| role.role == "example")
.expect("example role contract");
assert_eq!(contract.startup_skill.as_deref(), Some("example"));
assert_eq!(
contract.startup_skill_sha256.as_deref().map(str::len),
Some(64)
);
}
}
#[test]
fn yaml_reserved_startup_skill_identifiers_fail_before_emission() {
for reserved in [
"true",
"false",
"null",
"yes",
"no",
"on",
"off",
"0",
"123",
"2026-01-01",
] {
let mut subject = input();
subject.roles[1].startup_skill = reserved.into();
subject.skills[0].name = reserved.into();
subject.skills[0].source_path = format!("content/skills/{reserved}/SKILL.md");
let error = compile(&subject, &HarnessProfile::claude()).expect_err(reserved);
assert!(error.to_string().contains("identifier"), "{error}");
}
}
#[test]
fn empty_skill_resources_are_valid_and_digested() {
let mut subject = input();
subject.roles[1].startup_skill = "context".into();
subject.skills[0].resources = vec![
resource("assets/empty.txt", b"", false),
resource("references/empty.md", b"", false),
resource("scripts/empty.sh", b"", true),
];
let tree = compile(&subject, &HarnessProfile::claude()).expect("empty resources");
let coder = tree
.roles
.iter()
.find(|role| role.role == "coder")
.expect("coder contract");
assert_eq!(
coder.startup_skill_sha256.as_deref().map(str::len),
Some(64)
);
assert_eq!(
tree.files
.iter()
.filter(|file| file.path.contains("/empty."))
.map(|file| (file.path.as_str(), file.content.as_str(), file.mode))
.collect::<Vec<_>>(),
[
("skills/context/assets/empty.txt", "", 0o644),
("skills/context/references/empty.md", "", 0o644),
("skills/context/scripts/empty.sh", "", 0o755),
]
);
}
#[test]
fn startup_skill_must_exist_and_support_the_target() {
let mut unknown = input();
unknown.roles[1].startup_skill = "missing".into();
let error = compile(&unknown, &HarnessProfile::claude()).expect_err("unknown startup skill");
assert!(error.to_string().contains("unknown startup skill"));
let mut incompatible = input();
incompatible.roles[1].startup_skill = "harness".into();
let error = compile(&incompatible, &HarnessProfile::pi()).expect_err("Claude-only skill on Pi");
assert!(error.to_string().contains("not portable to target `pi`"));
}
#[test]
fn malformed_or_oversized_skill_resources_fail_closed() {
for (label, bad_resource, message) in [
(
"duplicate",
vec![
resource("references/contract.md", b"one\n", false),
resource("references/contract.md", b"two\n", false),
],
"duplicate skill resource",
),
(
"traversal",
vec![resource("references/../contract.md", b"bad\n", false)],
"resource path",
),
(
"reference-mode",
vec![resource("references/contract.md", b"bad\n", true)],
"executable",
),
(
"script-mode",
vec![resource("scripts/check.sh", b"bad\n", false)],
"executable",
),
(
"invalid-utf8",
vec![resource("assets/data.txt", &[0xff], false)],
"UTF-8",
),
(
"oversized",
vec![resource("assets/data.txt", &vec![b'x'; 65_537], false)],
"65536 bytes",
),
] {
let mut subject = input();
subject.skills[0].resources = bad_resource;
let error = compile(&subject, &HarnessProfile::claude()).expect_err(label);
assert!(error.to_string().contains(message), "{label}: {error}");
}
}