use super::*;
use std::fs;
use tempfile::TempDir;
fn write_agent(dir: &Path, name: &str, content: &str) {
fs::write(dir.join(format!("{name}.md")), content).expect("write agent");
}
#[test]
fn scan_finds_agents() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"engineer",
"---\nname: engineer\nrole: engineer\nextends: base-engineer\n\
description: Implements features and fixes bugs.\nmodel: sonnet\n---\n\n# Engineer\n",
);
write_agent(
tmp.path(),
"BASE-AGENT",
"---\nname: base-agent\nrole: base\n---\n\n# Base\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(agents.len(), 1, "only the engineer is deployable");
let engineer = &agents[0];
assert_eq!(engineer.name, "engineer");
assert_eq!(engineer.role, "engineer");
assert_eq!(
engineer.description.as_deref(),
Some("Implements features and fixes bugs.")
);
assert_eq!(engineer.model.as_deref(), Some("sonnet"));
assert_eq!(
engineer.extends_chain,
vec!["base-engineer".to_string(), "engineer".to_string()]
);
}
#[test]
fn scan_excludes_base_agents() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"BASE-AGENT",
"---\nname: base-agent\nrole: base\n---\n\nfoundation\n",
);
write_agent(
tmp.path(),
"base-engineer",
"---\nname: base-engineer\nrole: base-engineer\n---\n\nfoundation\n",
);
write_agent(
tmp.path(),
"qa",
"---\nname: qa\nrole: qa\ndescription: Tests things.\n---\n\n# QA\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(agents.len(), 1);
assert_eq!(agents[0].name, "qa");
}
#[test]
fn agent_with_a_base_role_but_no_base_filename_stays_in_the_roster() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"memory-manager",
"---\nname: memory-manager\nrole: base\ndescription: Manages memory.\n---\n\n# MM\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(
agents.iter().map(|a| a.name.as_str()).collect::<Vec<_>>(),
vec!["memory-manager"],
"a non-`BASE-*` file must never be classified as a foundation \
template by its `role:` value alone (#4589)"
);
}
#[test]
fn near_miss_base_names_are_not_treated_as_foundation_files() {
let tmp = TempDir::new().unwrap();
let kept = [
"baseline-analyzer",
"base64-decoder",
"basecamp-sync",
"based-reviewer",
"database-migrator",
"engineer",
];
let excluded = [
"BASE-AGENT",
"BASE-ENGINEER",
"BASE-OPS",
"BASE-QA",
"BASE-RESEARCH",
"base-custom",
"Base-Custom-Two",
];
for name in kept.iter().chain(excluded.iter()) {
write_agent(
tmp.path(),
name,
&format!("---\nname: {name}\nrole: {name}\n---\n\n# {name}\n"),
);
}
let mut scanned: Vec<String> = scan_agents(tmp.path())
.into_iter()
.map(|a| a.name)
.collect();
scanned.sort();
let mut want: Vec<String> = kept.iter().map(|s| s.to_string()).collect();
want.sort();
assert_eq!(
scanned, want,
"only `base-`-prefixed files are foundation templates; a bare \
`base` prefix silently deletes real agents (#4589 review)"
);
}
#[test]
fn scan_empty_dir() {
let tmp = TempDir::new().unwrap();
let agents = scan_agents(tmp.path());
assert!(agents.is_empty());
}
#[test]
fn scan_missing_dir_is_empty() {
let agents = scan_agents(Path::new("/no/such/agents/dir/xyz"));
assert!(agents.is_empty());
}
#[test]
fn scan_ignores_manifest_and_non_md() {
let tmp = TempDir::new().unwrap();
fs::write(tmp.path().join("manifest.json"), "{}").unwrap();
fs::write(tmp.path().join("notes.txt"), "hello").unwrap();
write_agent(
tmp.path(),
"writer",
"---\nname: writer\nrole: writer\n---\n\n# Writer\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(agents.len(), 1);
assert_eq!(agents[0].name, "writer");
}
#[test]
fn scan_handles_no_frontmatter() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"plain",
"# Plain agent\n\nNo frontmatter here.\n",
);
let agents = scan_agents(tmp.path());
assert!(
agents.is_empty(),
"a file with no frontmatter has no `name:`, so it is not dispatchable \
and must not enter the roster (#4711)"
);
}
#[test]
fn bare_base_md_is_excluded_from_the_roster() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"base",
"---\nname: base\nrole: base\n---\n\n# Base QA Instructions\n\n\
Appended to all QA agents.\n",
);
write_agent(
tmp.path(),
"Base",
"---\nname: Base\n---\n\n# Mixed-case spelling of the same convention\n",
);
write_agent(
tmp.path(),
"engineer",
"---\nname: engineer\nrole: engineer\n---\n\n# Engineer\n",
);
let names: Vec<String> = scan_agents(tmp.path())
.into_iter()
.map(|a| a.name)
.collect();
assert_eq!(
names,
vec!["engineer".to_string()],
"a bare `base.md` is a foundation file in every spelling (#4711)"
);
assert!(is_foundation_file("base"));
assert!(is_foundation_file("BASE"));
}
#[test]
fn base_prefixed_near_misses_survive_the_bare_base_rule() {
for stem in [
"baseline-analyzer",
"base64-decoder",
"basecamp-sync",
"based-reviewer",
] {
assert!(
!is_foundation_file(stem),
"`{stem}` is an ordinary agent name, not a foundation template (#4589)"
);
}
for stem in ["BASE-AGENT", "base-custom", "Base-Custom-Two"] {
assert!(
is_foundation_file(stem),
"`{stem}` is a foundation template"
);
}
}
#[test]
fn file_without_name_frontmatter_is_excluded_from_the_roster() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"shared-qa-preamble",
"---\nrole: qa\ndescription: Appended to all QA agents.\n---\n\n# Preamble\n",
);
write_agent(
tmp.path(),
"real-agent",
"---\nname: real-agent\ndescription: Does real work.\n---\n\n# Real\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(
agents.len(),
1,
"only the file declaring `name:` is a dispatchable agent (#4711)"
);
assert_eq!(agents[0].name, "real-agent");
assert_eq!(
agents[0].description.as_deref(),
Some("Does real work."),
"a real agent with `name:` frontmatter is still admitted unchanged"
);
}
#[test]
fn generate_authority_nonempty() {
let agents = vec![AgentSummary {
name: "engineer".to_string(),
role: "engineer".to_string(),
description: Some("Implements features.".to_string()),
model: Some("sonnet".to_string()),
extends_chain: vec![
"base-agent".to_string(),
"base-engineer".to_string(),
"engineer".to_string(),
],
}];
let md = generate_authority(&agents);
assert!(md.contains("## Delegation Authority"));
assert!(md.contains("### engineer"));
assert!(md.contains("base-agent → base-engineer → engineer"));
assert!(md.contains("**Model:** sonnet"));
}
#[test]
fn generate_authority_empty() {
let md = generate_authority(&[]);
assert!(md.contains("## Delegation Authority"));
assert!(md.to_lowercase().contains("no delegatable agents"));
}
#[test]
fn scan_preserves_url_in_description() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"docs-agent",
"---\nname: docs-agent\nrole: docs\ndescription: See https://docs.example.com/guide\n---\n\n# Docs\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(agents.len(), 1);
assert_eq!(
agents[0].description.as_deref(),
Some("See https://docs.example.com/guide"),
"URL in description must not be truncated"
);
}
#[test]
fn scan_preserves_bedrock_model_id() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"ml-agent",
"---\nname: ml-agent\nrole: ml\nmodel: bedrock/us.anthropic.claude-sonnet-4-6\n---\n\n# ML\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(agents.len(), 1);
assert_eq!(
agents[0].model.as_deref(),
Some("bedrock/us.anthropic.claude-sonnet-4-6"),
"bedrock model id must be preserved verbatim"
);
}
#[test]
fn scan_preserves_timestamp_in_description() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"timed-agent",
"---\nname: timed-agent\nrole: timer\ndescription: Deployed at 2026-06-05T14:31:34\n---\n\n# Timed\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(agents.len(), 1);
assert_eq!(
agents[0].description.as_deref(),
Some("Deployed at 2026-06-05T14:31:34"),
"timestamp in description must not be truncated"
);
}
#[test]
fn every_bundled_non_base_agent_reaches_the_roster() {
let assets = crate::core::content_source::test_support::repo_agents_dir();
let mut expected: Vec<String> = fs::read_dir(&assets)
.expect("bundled agent assets dir")
.flatten()
.map(|e| e.path())
.filter(|p| p.extension().and_then(|e| e.to_str()) == Some("md"))
.filter_map(|p| {
p.file_stem()
.and_then(|s| s.to_str())
.filter(|stem| !is_foundation_file(stem))
.map(str::to_string)
})
.collect();
expected.sort();
let mut scanned: Vec<String> = scan_agents(&assets).into_iter().map(|a| a.name).collect();
scanned.sort();
let dropped: Vec<&String> = expected.iter().filter(|n| !scanned.contains(n)).collect();
assert!(
dropped.is_empty(),
"bundled agents silently dropped from every roster (check for a stray \
`role: base` in their frontmatter): {dropped:?}"
);
assert_eq!(
scanned.len(),
expected.len(),
"every non-BASE bundled agent must be delegatable"
);
}
#[test]
fn generate_authority_omits_self_referential_lines() {
let agents = vec![AgentSummary {
name: "ticketing".to_string(),
role: "ticketing".to_string(),
description: Some("Files tickets.".to_string()),
model: None,
extends_chain: vec!["ticketing".to_string()],
}];
let md = generate_authority(&agents);
assert!(md.contains("### ticketing"));
assert!(
!md.contains("**Role:**"),
"role duplicating the heading must be omitted"
);
assert!(
!md.contains("**Foundation:**"),
"a self-referential single-element chain must be omitted"
);
}
#[test]
fn roster_from_dirs_dedup_is_case_insensitive() {
let high = TempDir::new().unwrap();
let low = TempDir::new().unwrap();
write_agent(
high.path(),
"QA",
"---\nname: QA\nrole: qa\ndescription: PROJECT TIER\n---\n\n# QA\n",
);
write_agent(
low.path(),
"qa",
"---\nname: qa\nrole: qa\ndescription: USER TIER\n---\n\n# QA\n",
);
let roster = roster_from_dirs(&[high.path().to_path_buf(), low.path().to_path_buf()]);
assert_eq!(roster.len(), 1, "QA and qa are the same agent");
assert_eq!(roster[0].description.as_deref(), Some("PROJECT TIER"));
}
#[test]
fn deployed_agent_dirs_puts_project_tier_first() {
let dirs = deployed_agent_dirs(Path::new("/proj"));
assert_eq!(dirs[0], PathBuf::from("/proj/.claude/agents"));
assert!(
dirs.len() >= 2,
"the user-level tier(s) must also be consulted"
);
}
#[test]
fn deployed_agent_dirs_from_is_pure_and_ordered() {
let dirs = deployed_agent_dirs_from(
Path::new("/proj"),
Some(Path::new("/managed/claude-config")),
Path::new("/home/u/.claude/agents"),
);
assert_eq!(
dirs,
vec![
PathBuf::from("/proj/.claude/agents"),
PathBuf::from("/managed/claude-config/agents"),
PathBuf::from("/home/u/.claude/agents"),
]
);
}
#[test]
fn deployed_agent_dirs_from_skips_absent_managed_tier() {
let dirs = deployed_agent_dirs_from(
Path::new("/proj"),
None,
Path::new("/home/u/.claude/agents"),
);
assert_eq!(
dirs,
vec![
PathBuf::from("/proj/.claude/agents"),
PathBuf::from("/home/u/.claude/agents"),
]
);
}
#[test]
fn roster_from_dirs_dedupes_with_first_dir_winning() {
let high = TempDir::new().unwrap();
let low = TempDir::new().unwrap();
write_agent(
high.path(),
"qa",
"---\nname: qa\nrole: qa\ndescription: PROJECT TIER\n---\n\n# QA\n",
);
write_agent(
low.path(),
"qa",
"---\nname: qa\nrole: qa\ndescription: USER TIER\n---\n\n# QA\n",
);
write_agent(
low.path(),
"ops",
"---\nname: ops\nrole: ops\ndescription: USER TIER\n---\n\n# Ops\n",
);
let roster = roster_from_dirs(&[high.path().to_path_buf(), low.path().to_path_buf()]);
assert_eq!(roster.len(), 2, "qa must appear once, plus ops");
assert_eq!(roster[0].name, "ops", "roster is name-sorted");
assert_eq!(roster[1].name, "qa");
assert_eq!(roster[1].description.as_deref(), Some("PROJECT TIER"));
}
#[test]
fn roster_from_dirs_ignores_missing_dirs() {
let roster = roster_from_dirs(&[PathBuf::from("/nonexistent/agents")]);
assert!(roster.is_empty());
assert!(roster_from_dirs(&[]).is_empty());
}
#[test]
fn generate_authority_omits_the_harness_supplied_description() {
let agents = vec![AgentSummary {
name: "api-qa".to_string(),
role: "qa".to_string(),
description: Some(
"Specialized API and backend testing for REST, GraphQL, and \
server-side functionality"
.to_string(),
),
model: Some("sonnet".to_string()),
extends_chain: vec!["api-qa".to_string()],
}];
let md = generate_authority(&agents);
assert!(md.contains("### api-qa"));
assert!(
md.contains("**Role:** qa"),
"role is the roster's own value"
);
assert!(
md.contains("**Model:** sonnet"),
"model is the roster's own value"
);
assert!(
!md.contains("**Handles:**"),
"the harness-supplied description must not be re-emitted"
);
assert!(
!md.contains("Specialized API and backend testing"),
"the description text itself must not survive under another label"
);
}
#[test]
fn generate_authority_renders_an_agent_with_neither_role_nor_model() {
let agents = vec![
AgentSummary {
name: "alpha".to_string(),
role: "alpha".to_string(),
description: Some("ignored".to_string()),
model: None,
extends_chain: vec!["alpha".to_string()],
},
AgentSummary {
name: "beta".to_string(),
role: "beta".to_string(),
description: None,
model: None,
extends_chain: vec!["beta".to_string()],
},
];
let md = generate_authority(&agents);
assert!(md.contains("### alpha\n\n### beta\n"), "got: {md:?}");
assert!(!md.contains("(no description provided)"));
}
#[test]
fn block_scalar_description_is_folded() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"copyeditor",
"---\nname: copyeditor\nmodel: sonnet\ndescription: >\n \
Line/word-level copyedit pass.\n Assumes structure is sound.\n---\n\n# Copyeditor\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(agents.len(), 1);
assert_eq!(
agents[0].description.as_deref(),
Some("Line/word-level copyedit pass. Assumes structure is sound."),
"a folded scalar joins its lines with a space"
);
assert_eq!(agents[0].model.as_deref(), Some("sonnet"));
}
#[test]
fn literal_block_scalar_description_keeps_its_line_breaks() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"writer",
"---\nname: writer\ndescription: |-\n first line\n second line\nmodel: opus\n---\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(
agents[0].description.as_deref(),
Some("first line\nsecond line")
);
assert_eq!(
agents[0].model.as_deref(),
Some("opus"),
"the key AFTER the block scalar must still parse"
);
}
#[test]
fn block_scalar_body_stops_at_the_closing_fence() {
let tmp = TempDir::new().unwrap();
write_agent(
tmp.path(),
"proofreader",
"---\nname: proofreader\ndescription: >\n only this\n---\n\n# Proofreader\n\nBody prose.\n",
);
let agents = scan_agents(tmp.path());
assert_eq!(agents[0].description.as_deref(), Some("only this"));
}
#[test]
fn a_plain_value_beginning_with_an_angle_bracket_is_not_a_block_scalar() {
assert!(is_block_scalar_header(">"));
assert!(is_block_scalar_header("|"));
assert!(is_block_scalar_header(">-"));
assert!(is_block_scalar_header("|+"));
assert!(is_block_scalar_header(">2"));
assert!(!is_block_scalar_header("> quoted prose"));
assert!(!is_block_scalar_header("|pipe|table|"));
assert!(!is_block_scalar_header("sonnet"));
}
fn deny_read(path: &Path) -> bool {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if fs::set_permissions(path, fs::Permissions::from_mode(0o000)).is_err() {
return false;
}
fs::read_to_string(path).is_err() || fs::read_dir(path).is_err()
}
#[cfg(not(unix))]
{
let _ = path;
false
}
}
#[test]
fn an_unreadable_agent_file_is_reported_not_silently_dropped() {
let tmp = TempDir::new().unwrap();
write_agent(tmp.path(), "engineer", "---\nname: engineer\n---\n\n# E\n");
write_agent(tmp.path(), "qa", "---\nname: qa\n---\n\n# Q\n");
let locked = tmp.path().join("qa.md");
if !deny_read(&locked) {
eprintln!("skipping: cannot deny read on this platform/privilege level");
return;
}
let scan = scan_agents_reporting(tmp.path());
assert_eq!(
scan.agents
.iter()
.map(|a| a.name.as_str())
.collect::<Vec<_>>(),
vec!["engineer"],
"the unreadable agent is still absent from the roster (fail-open)"
);
assert_eq!(
scan.unreadable,
vec![locked.clone()],
"but the loss must be REPORTED, not absorbed into a short list"
);
let section = roster_section_from_dirs(&[tmp.path().to_path_buf()])
.expect("a roster with one agent renders a section");
assert!(
section.contains(ROSTER_INCOMPLETE_MARKER),
"the composed delegation section must declare itself incomplete; got:\n{section}"
);
assert!(
section.contains("qa.md"),
"the banner must name the path that was lost; got:\n{section}"
);
}
#[test]
fn an_unreadable_tier_directory_is_reported_not_silently_dropped() {
let present = TempDir::new().unwrap();
write_agent(
present.path(),
"engineer",
"---\nname: engineer\n---\n\n# E\n",
);
let denied = TempDir::new().unwrap();
write_agent(
denied.path(),
"ticketing",
"---\nname: ticketing\n---\n\n# T\n",
);
if !deny_read(denied.path()) {
eprintln!("skipping: cannot deny read on this platform/privilege level");
return;
}
let scan =
roster_from_dirs_reporting(&[present.path().to_path_buf(), denied.path().to_path_buf()]);
assert_eq!(scan.agents.len(), 1, "only the readable tier contributes");
assert_eq!(
scan.unreadable,
vec![denied.path().to_path_buf()],
"the unreadable TIER must be named, not collapsed into 'empty'"
);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let _ = fs::set_permissions(denied.path(), fs::Permissions::from_mode(0o700));
}
}
#[test]
fn a_roster_that_is_empty_because_reads_failed_still_renders_a_section() {
let tmp = TempDir::new().unwrap();
write_agent(tmp.path(), "engineer", "---\nname: engineer\n---\n\n# E\n");
if !deny_read(tmp.path()) {
eprintln!("skipping: cannot deny read on this platform/privilege level");
return;
}
let section = roster_section_from_dirs(&[tmp.path().to_path_buf()]);
let section = section.expect("an unreadable tier must NOT degrade silently to None");
assert!(
section.contains(ROSTER_INCOMPLETE_MARKER),
"got:\n{section}"
);
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let _ = fs::set_permissions(tmp.path(), fs::Permissions::from_mode(0o700));
}
}
#[test]
fn roster_section_from_dirs_is_none_when_nothing_is_deployed() {
assert!(roster_section_from_dirs(&[PathBuf::from("/nonexistent/agents")]).is_none());
assert!(roster_section_from_dirs(&[]).is_none());
}
#[test]
fn generate_authority_is_unchanged_when_nothing_was_lost() {
let agents = vec![AgentSummary {
name: "qa".to_string(),
role: "qa".to_string(),
description: None,
model: Some("sonnet".to_string()),
extends_chain: vec!["qa".to_string()],
}];
assert_eq!(
generate_authority_reporting(&agents, &[]),
generate_authority(&agents)
);
assert!(!generate_authority(&agents).contains(ROSTER_INCOMPLETE_MARKER));
}
fn harness_and_extra_tiers() -> (TempDir, TempDir) {
(TempDir::new().unwrap(), TempDir::new().unwrap())
}
#[test]
fn harness_injected_agents_are_not_repeated_in_the_roster() {
let (harness, extra) = harness_and_extra_tiers();
write_agent(
harness.path(),
"rust-engineer",
"---\nname: rust-engineer\nrole: engineer\nmodel: opus\n---\n",
);
write_agent(
extra.path(),
"copyeditor",
"---\nname: copyeditor\nmodel: sonnet\n---\n",
);
let dirs = vec![harness.path().to_path_buf(), extra.path().to_path_buf()];
let harness_dirs = vec![harness.path().to_path_buf()];
let section = roster_section_from_tiers(&dirs, &harness_dirs).expect("section");
assert!(
!section.contains("### rust-engineer"),
"the harness publishes rust-engineer; the roster must not repeat it:\n{section}"
);
assert!(
section.contains("### copyeditor"),
"copyeditor is in no harness tier, so only the roster can name it:\n{section}"
);
assert!(
section.contains("Available agent types for the Agent tool"),
"the PM must be told where the authoritative listing is:\n{section}"
);
assert!(
section.contains("the 1 agents it carries"),
"the count of omitted agents must be stated:\n{section}"
);
}
#[test]
fn a_cut_agent_stays_in_the_resolved_roster_and_so_stays_dispatchable() {
let (harness, extra) = harness_and_extra_tiers();
write_agent(
harness.path(),
"ticketing",
"---\nname: ticketing\nmodel: sonnet\n---\n",
);
write_agent(extra.path(), "writer", "---\nname: writer\n---\n");
let dirs = vec![harness.path().to_path_buf(), extra.path().to_path_buf()];
let names: Vec<String> = roster_from_dirs(&dirs)
.into_iter()
.map(|a| a.name)
.collect();
assert_eq!(
names,
vec!["ticketing".to_string(), "writer".to_string()],
"the resolved roster is unfiltered; only the rendered section is"
);
}
#[test]
fn a_fully_harness_injected_roster_points_at_the_harness_listing() {
let (harness, extra) = harness_and_extra_tiers();
write_agent(harness.path(), "qa", "---\nname: qa\nmodel: sonnet\n---\n");
let dirs = vec![harness.path().to_path_buf(), extra.path().to_path_buf()];
let harness_dirs = vec![harness.path().to_path_buf()];
let section = roster_section_from_tiers(&dirs, &harness_dirs).expect("section still renders");
assert!(section.starts_with("## Delegation Authority"));
assert!(
!section.contains("### qa"),
"qa is harness-published:\n{section}"
);
assert!(
section.contains("Available agent types for the Agent tool"),
"an all-harness roster must still name the routing surface:\n{section}"
);
assert!(
!section.contains("No delegatable agents"),
"agents ARE deployed; claiming none is the failure this guards:\n{section}"
);
}
#[test]
fn an_agent_in_both_tiers_counts_as_harness_injected() {
let (harness, extra) = harness_and_extra_tiers();
write_agent(harness.path(), "qa", "---\nname: qa\nmodel: sonnet\n---\n");
write_agent(extra.path(), "QA", "---\nname: QA\nmodel: haiku\n---\n");
let dirs = vec![extra.path().to_path_buf(), harness.path().to_path_buf()];
let harness_dirs = vec![harness.path().to_path_buf()];
let section = roster_section_from_tiers(&dirs, &harness_dirs).expect("section");
assert!(
!section.contains("### QA") && !section.contains("### qa"),
"the harness carries this name; the case of the winning file is irrelevant:\n{section}"
);
}
#[test]
fn roster_section_from_tiers_with_no_harness_tier_matches_the_undeduped_render() {
let (a, b) = harness_and_extra_tiers();
write_agent(
a.path(),
"writer",
"---\nname: writer\nmodel: sonnet\n---\n",
);
write_agent(b.path(), "proofreader", "---\nname: proofreader\n---\n");
let dirs = vec![a.path().to_path_buf(), b.path().to_path_buf()];
assert_eq!(
roster_section_from_tiers(&dirs, &[]),
roster_section_from_dirs(&dirs)
);
}
#[test]
fn roster_section_from_tiers_is_none_when_nothing_is_deployed() {
assert!(roster_section_from_tiers(&[PathBuf::from("/nonexistent/agents")], &[]).is_none());
assert!(roster_section_from_tiers(&[], &[]).is_none());
}
#[test]
fn generate_authority_is_unchanged_when_nothing_was_deduped() {
let agents = vec![AgentSummary {
name: "qa".to_string(),
role: "qa".to_string(),
description: None,
model: Some("sonnet".to_string()),
extends_chain: vec!["qa".to_string()],
}];
assert_eq!(
generate_authority_deduped(&agents, 0, &[]),
generate_authority(&agents)
);
}
#[test]
fn harness_loaded_dirs_follow_claude_config_dir() {
let dirs = harness_loaded_agent_dirs_from(
Path::new("/proj"),
Some(Path::new("/managed")),
Path::new("/home/.claude/agents"),
);
assert_eq!(
dirs,
vec![
PathBuf::from("/proj/.claude/agents"),
PathBuf::from("/managed/agents"),
]
);
assert!(
!dirs.contains(&PathBuf::from("/home/.claude/agents")),
"the home tier is invisible to the harness once CLAUDE_CONFIG_DIR is set"
);
}
#[test]
fn harness_loaded_dirs_fall_back_to_the_home_tier() {
let dirs =
harness_loaded_agent_dirs_from(Path::new("/proj"), None, Path::new("/home/.claude/agents"));
assert_eq!(
dirs,
vec![
PathBuf::from("/proj/.claude/agents"),
PathBuf::from("/home/.claude/agents"),
]
);
}