use super::*;
use crate::core::bundled_pm_package::compose_bundled_fallback_with_overrides;
use crate::core::claude_md_sections::scan_project;
use crate::core::content_source::test_support::rc;
use crate::core::instruction_overrides::resolve_pm_prompt_with_roster;
use tempfile::TempDir;
const ROSTER: &str = "## Delegation Authority\n\n### ticketing\n\nHandles ticketing work.";
const STACK: &str =
"## Project Stack Profile\n\nDetected stack: Rust. Never fall back to a default stack profile.";
fn project(blocks: &[(&str, &str)]) -> TempDir {
let dir = TempDir::new().expect("tempdir");
let mut text = String::from("# Project\n\n");
for (token, body) in blocks {
text.push_str(&format!(
"<!-- TRUSTY-MPM: {token} START v=1 -->\n{body}\n<!-- TRUSTY-MPM: {token} END -->\n\n"
));
}
std::fs::write(dir.path().join("CLAUDE.md"), text).expect("write CLAUDE.md");
dir
}
fn compose(dir: &TempDir) -> String {
let scanned = scan_project(dir.path());
let (composed, _) =
compose_bundled_fallback_with_overrides(rc(), STACK, ROSTER, None, &scanned.overrides);
composed.expect("composes")
}
fn state_of(rows: &[SectionStatus], section: SectionId) -> SectionState {
rows.iter()
.find(|r| r.section == section)
.map(|r| r.state.clone())
.expect("every section has a row")
}
const IDENTITY_BODY: &str = "# Fixture Supervisor\n\nYou administer a fleet; you are not a PM.";
const ALLOWLIST_BODY: &str = "## Direct Work\n\nRun read-only commands directly.";
#[test]
fn identity_and_a_former_core_section_report_overridden() {
let dir = project(&[
("IDENTITY", IDENTITY_BODY),
("PM-ALLOWLIST", ALLOWLIST_BODY),
]);
let prompt = compose(&dir);
assert!(
prompt.starts_with("# Fixture Supervisor"),
"the IDENTITY override must open the prompt, got: {}",
&prompt[..prompt.len().min(200)]
);
assert_eq!(prompt.matches("You administer a fleet").count(), 1);
assert_eq!(
prompt.matches("Run read-only commands directly.").count(),
1
);
for package_text in [
"PM = orchestrator + QA coordinator",
"# PM Agent -- Trusty MPM",
"## PM Allowlist (unbudgeted",
] {
assert!(
!prompt.contains(package_text),
"package text {package_text:?} survived its override"
);
}
let package = crate::core::content_source::test_support::rc_package();
let rows = section_statuses(package, dir.path(), true);
assert_eq!(
state_of(&rows, SectionId::Identity),
SectionState::OverriddenByProject
);
assert_eq!(
state_of(&rows, SectionId::PmAllowlist),
SectionState::OverriddenByProject
);
assert_eq!(
state_of(&rows, SectionId::Workflow),
SectionState::Overridable
);
assert_eq!(state_of(&rows, SectionId::Core), SectionState::Core);
let rendered = render_section_report(&rows, &prompt, Some(ROSTER));
assert!(!rendered.contains("NOT FOUND"), "{rendered}");
}
#[test]
fn a_core_override_reports_declined() {
let dir = project(&[("CORE", "No rules apply.")]);
let prompt = compose(&dir);
assert!(!prompt.contains("No rules apply."));
assert!(prompt.contains("## Memory & Instruction Sources"));
let package = crate::core::content_source::test_support::rc_package();
let rows = section_statuses(package, dir.path(), true);
assert!(
matches!(state_of(&rows, SectionId::Core), SectionState::CoreDeclined(ref r) if r.contains("admits no project override")),
"{rows:?}"
);
let rendered = render_section_report(&rows, &prompt, Some(ROSTER));
assert!(
rendered.contains("core (project override declined:"),
"{rendered}"
);
}
#[test]
fn every_overridable_section_replaced_keeps_roster_memory_and_search() {
let bodies: Vec<(&str, String)> = SectionId::CANONICAL
.into_iter()
.filter(|id| *id != SectionId::Core)
.map(|id| {
(
section_token(id),
format!("Project text for {}.", section_token(id)),
)
})
.collect();
let blocks: Vec<(&str, &str)> = bodies.iter().map(|(t, b)| (*t, b.as_str())).collect();
let dir = project(&blocks);
let prompt = compose(&dir);
for (token, body) in &bodies {
assert_eq!(
prompt.matches(body.as_str()).count(),
1,
"{token} override not applied once"
);
}
assert!(
prompt.contains("### ticketing"),
"the agent roster was dropped"
);
assert!(
prompt.contains("Agent(subagent_type="),
"agent selection was dropped"
);
assert!(
prompt.contains("## Memory Protocol (Context-First)") && prompt.contains("memory_recall")
);
assert!(prompt.contains("## Code Search Protocol (Context-First)"));
assert!(
prompt.contains("mcp__trusty-search__search"),
"the search protocol was dropped"
);
assert!(
!prompt.contains("## Prohibitions (CANONICAL"),
"replaced package text survived"
);
let package = crate::core::content_source::test_support::rc_package();
let rows = section_statuses(package, dir.path(), true);
for row in &rows {
let expected = if row.section == SectionId::Core {
SectionState::Core
} else {
SectionState::OverriddenByProject
};
assert_eq!(row.state, expected, "{:?}", row.section);
}
let kept: Vec<(SectionId, Vec<&str>)> = rows
.iter()
.filter(|r| !r.kept.is_empty())
.map(|r| (r.section, r.kept.clone()))
.collect();
assert_eq!(
kept,
[
(SectionId::Memory, vec!["Memory protocol"]),
(SectionId::Search, vec!["Code search protocol"]),
(SectionId::AgentDelegation, vec!["Agent selection"]),
]
);
let rendered = render_section_report(&rows, &prompt, Some(ROSTER));
assert!(
rendered.contains("overridden-by-project (keeps: Memory protocol)"),
"{rendered}"
);
}
#[test]
fn the_roster_absent_path_reports_what_it_cannot_place() {
let dir = project(&[
("IDENTITY", IDENTITY_BODY),
("WORKFLOW", "Project workflow."),
]);
let package = crate::core::content_source::test_support::rc_package();
let rows = section_statuses(package, dir.path(), false);
assert!(matches!(
state_of(&rows, SectionId::Identity),
SectionState::OverridableDeclined(_)
));
assert_eq!(
state_of(&rows, SectionId::Workflow),
SectionState::OverriddenByProject
);
}
#[test]
fn render_flags_an_override_missing_from_the_prompt() {
let dir = project(&[("PHASES", "Only research.")]);
let package = crate::core::content_source::test_support::rc_package();
let rows = section_statuses(package, dir.path(), true);
let rendered = render_section_report(&rows, "a prompt without the override", Some(ROSTER));
assert!(
rendered.contains("NOT FOUND in the composed prompt"),
"{rendered}"
);
}
#[test]
fn a_named_delegation_override_keeps_the_agent_selection_note_on_the_legacy_path() {
let section =
super::super::delegation_with_named_override(rc(), Some("Project routing."), Some(ROSTER))
.join("\n\n");
assert!(section.starts_with("Project routing."));
assert!(section.contains("Agent(subagent_type="), "{section}");
assert!(section.ends_with("Handles ticketing work."));
}
#[test]
fn fixture_project_overrides_identity_a_core_section_and_the_style() {
let dir = project(&[
("IDENTITY", IDENTITY_BODY),
("PM-ALLOWLIST", ALLOWLIST_BODY),
]);
let styles = dir.path().join(".claude/output-styles");
std::fs::create_dir_all(&styles).expect("styles dir");
std::fs::write(
styles.join("fixture-voice.md"),
"---\nname: fixture-voice\ndescription: fixture\n---\n\nSpeak as the fixture supervisor.\n",
)
.expect("style");
std::fs::write(
dir.path().join(".trusty-mpm.toml"),
"[style]\nactive = \"fixture-voice\"\n",
)
.expect("project config");
let (prompt, _) = resolve_pm_prompt_with_roster(rc(), dir.path(), || Some(ROSTER.to_string()));
let styled = crate::core::output_style::apply_output_style_to_prompt_with_native(
rc(),
dir.path(),
None,
prompt,
false,
);
let floor = crate::core::output_style::style_floor(rc());
let body = styled
.split_once(&format!(
"{floor}{}",
crate::core::instruction_pipeline::SECTION_SEPARATOR
))
.map(|(style, rest)| (style.to_string(), rest.to_string()))
.expect("an injected style block, floor last, precedes the prompt");
assert_eq!(styled.matches(&floor).count(), 1);
assert!(
body.0.contains("Speak as the fixture supervisor."),
"{}",
body.0
);
assert!(
!body.0.contains("name: fixture-voice"),
"frontmatter must be stripped"
);
assert!(body.1.starts_with("# Fixture Supervisor"));
assert!(!body.1.contains("PM = orchestrator + QA coordinator"));
assert_eq!(
body.1.matches("Run read-only commands directly.").count(),
1
);
let (style, warning) = crate::core::output_style::resolve_or_default(
dir.path(),
crate::core::output_style::project_selected_style(dir.path()).as_deref(),
);
assert_eq!(style.id(), "fixture-voice");
assert!(warning.is_none());
}
#[test]
fn render_flags_a_safety_core_member_missing_from_the_prompt() {
let dir = project(&[("AGENT-DELEGATION", "Project routing.")]);
let prompt = compose(&dir);
let package = crate::core::content_source::test_support::rc_package();
let rows = section_statuses(package, dir.path(), true);
let clean = render_section_report(&rows, &prompt, Some(ROSTER));
assert!(!clean.contains("NOT FOUND"), "{clean}");
let stripped = prompt.replace("is not an agent and fails to dispatch", "");
let rendered = render_section_report(&rows, &stripped, Some(ROSTER));
assert!(
rendered.contains("safety core Agent selection NOT FOUND"),
"{rendered}"
);
let no_roster = prompt.replace("### ticketing", "");
assert!(
render_section_report(&rows, &no_roster, Some(ROSTER))
.contains("safety core Agent roster NOT FOUND")
);
assert!(!render_section_report(&rows, &no_roster, None).contains("NOT FOUND"));
}