use super::*;
use crate::core::instruction_overrides::{
FILE_AGENT_DELEGATION, FILE_INSTRUCTIONS, FILE_MEMORY, FILE_WORKFLOW, OVERRIDE_DIR_NAME,
PromptSource, assemble_sections, delegation_with_roster, resolve_pm_prompt,
resolve_pm_prompt_with_roster, resolve_pm_prompt_with_source,
};
use crate::core::instruction_package::{
BlockBody, CustomizationTier, Generator, InstructionBlock, OverrideTier, SCHEMA_VERSION,
ValidationError,
};
use crate::core::instruction_pipeline::{
AGENT_DELEGATION, SECTION_CORE, SECTION_ENFORCEMENT, SECTION_FRAMEWORK_CONVENTIONS,
SECTION_IDENTITY, SECTION_MEMORY, SECTION_NON_OVERRIDABLE_RULES, SECTION_SEARCH,
SECTION_SOURCES, WORKFLOW, section_source, workflow_section,
};
use crate::core::stack_profile::stack_profile_section;
use std::fs;
use std::path::Path;
use tempfile::TempDir;
fn package_ref() -> &'static InstructionPackage {
bundled_fallback_package().expect("the bundled manifest parses and validates")
}
fn authored(block: &InstructionBlock) -> Option<&str> {
block
.body
.authored()
.map(|body| body.expect("source resolves"))
}
fn compose_bundled_fallback(
stack: &str,
roster: &str,
addendum: Option<&str>,
) -> Result<String, CompositionError> {
compose_bundled_fallback_with_overrides(stack, roster, addendum, &[]).0
}
const FIXED_ROSTER: &str = "## Delegation Authority\n\n\
### ticketing\n\nHandles ticketing work. Model: sonnet.\n\n\
### rust-engineer\n\nHandles Rust work. Model: sonnet.";
const FIXED_STACK: &str =
"## Project Stack Profile\n\nDetected stack: Rust. Route implementation to `rust-engineer`.";
const FIXED_ADDENDUM: &str = "# Project Rules\n\nALWAYS_RUN_MAKE_CHECK";
fn legacy_bundled_fallback(stack: &str, roster: &str, addendum: Option<&str>) -> String {
assemble_sections(
stack.to_string(),
None,
workflow_section().to_string(),
delegation_with_roster(Some(roster)),
addendum.map(str::to_string),
)
}
fn assert_byte_identical(expected: &str, actual: &str) {
if expected == actual {
return;
}
let (e, a) = (expected.as_bytes(), actual.as_bytes());
let at = e
.iter()
.zip(a.iter())
.position(|(x, y)| x != y)
.unwrap_or_else(|| e.len().min(a.len()));
const WINDOW: usize = 120;
let from = at.saturating_sub(WINDOW);
let window =
|s: &[u8]| String::from_utf8_lossy(&s[from..s.len().min(at + WINDOW)]).replace('\n', "\\n");
panic!(
"composed prompt is NOT byte-identical to the legacy assembly.\n\
first difference at byte offset {at} (expected {} bytes, actual {} bytes)\n\
expected [{from}..]: {}\n\
actual [{from}..]: {}\n",
e.len(),
a.len(),
window(e),
window(a),
);
}
#[test]
fn composed_package_is_byte_identical_to_the_legacy_bundled_fallback() {
let composed =
compose_bundled_fallback(FIXED_STACK, FIXED_ROSTER, None).expect("package composes");
let legacy = legacy_bundled_fallback(FIXED_STACK, FIXED_ROSTER, None);
assert_byte_identical(&legacy, &composed);
}
#[test]
fn composed_package_is_byte_identical_with_a_project_addendum() {
let composed = compose_bundled_fallback(FIXED_STACK, FIXED_ROSTER, Some(FIXED_ADDENDUM))
.expect("package composes");
let legacy = legacy_bundled_fallback(FIXED_STACK, FIXED_ROSTER, Some(FIXED_ADDENDUM));
assert_byte_identical(&legacy, &composed);
assert!(composed.contains("ALWAYS_RUN_MAKE_CHECK"));
}
#[test]
fn bundled_manifest_parses_and_validates() {
let package = InstructionPackage::from_json(PM_PACKAGE_JSON)
.expect("the shipped manifest is valid schema-v2 JSON");
assert_eq!(package.validate(), Ok(()));
assert_eq!(package.schema_version, SCHEMA_VERSION);
assert_eq!(&package, package_ref());
assert!(
compose_bundled_fallback(FIXED_STACK, FIXED_ROSTER, None).is_ok(),
"the shipped manifest must compose"
);
}
#[test]
fn manifest_prose_lives_in_markdown_not_in_the_json() {
assert!(
PM_PACKAGE_JSON.len() < SECTION_CORE.len(),
"the manifest ({} bytes) must be smaller than core.md ({} bytes) — prose \
belongs in markdown",
PM_PACKAGE_JSON.len(),
SECTION_CORE.len()
);
for (path, _) in SECTION_SOURCES {
assert!(
PM_PACKAGE_JSON.contains(path),
"{path} must be referenced by a `file` body"
);
}
for block in &package_ref().blocks {
if let BlockBody::Text { text } = &block.body {
assert!(
text.len() < 1_500,
"inline manifest text must be a short authored rule, not lifted prose \
({} bytes in {:?})",
text.len(),
block.section
);
}
}
}
#[test]
fn every_section_source_resolves() {
for (path, body) in SECTION_SOURCES {
assert_eq!(section_source(path), Some(body), "{path} must resolve");
assert!(!body.trim().is_empty(), "{path} must not be blank");
}
assert_eq!(section_source("sections/does-not-exist.md"), None);
}
#[test]
fn unknown_file_source_is_rejected() {
let mut package = package_ref().clone();
let index = package
.blocks
.iter()
.position(|b| matches!(b.body, BlockBody::File { .. }))
.expect("the manifest uses file bodies");
package.blocks[index].body = BlockBody::File {
path: "sections/typo.md".to_string(),
};
let section = package.blocks[index].section;
assert_eq!(
package.validate(),
Err(ValidationError::UnknownFileSource {
index,
section,
path: "sections/typo.md".to_string(),
})
);
}
#[test]
fn shipped_sections_build_and_validate() {
let package = package_ref();
assert_eq!(package.validate(), Ok(()));
assert_eq!(package.package_id, PACKAGE_ID);
assert!(!package.trailing_newline);
let ids: Vec<SectionId> = package.sections.iter().map(|s| s.id).collect();
assert_eq!(ids, SectionId::CANONICAL.to_vec());
for section in &package.sections {
let expected = if section.id == SectionId::Core {
CustomizationTier::Fixed
} else {
CustomizationTier::Project
};
assert_eq!(
section.customization_tier, expected,
"section {:?} declares the wrong tier",
section.id
);
}
}
#[test]
fn package_round_trips_through_json() {
let package = package_ref();
let json = package.to_json().expect("serialize");
let parsed = InstructionPackage::from_json(&json).expect("deserialize");
assert_eq!(&parsed, package);
let inputs = CompositionInputs {
agent_roster: FIXED_ROSTER.to_string(),
stack_profile: Some(FIXED_STACK.to_string()),
project_addendum: None,
};
assert_eq!(
parsed.compose(&inputs).expect("compose round-tripped"),
package.compose(&inputs).expect("compose original"),
);
}
#[test]
fn the_former_floor_sections_are_still_the_block_tail() {
let package = package_ref();
let tail = [
SectionId::Identity,
SectionId::Enforcement,
SectionId::NonOverridableRules,
SectionId::FrameworkGuaranteedConventions,
];
let first = package
.blocks
.len()
.checked_sub(tail.len())
.expect("the package has at least four blocks");
assert!(
package.blocks[first..]
.iter()
.all(|b| tail.contains(&b.section)),
"the four former-floor sections are still the block tail"
);
}
fn deploy_agent(project: &Path, name: &str) {
let dir = project.join(".claude").join("agents");
fs::create_dir_all(&dir).expect("create .claude/agents");
fs::write(
dir.join(format!("{name}.md")),
format!(
"---\nname: {name}\nrole: {name}\ndescription: Handles {name} work.\n\
model: sonnet\n---\n\n# {name}\n"
),
)
.expect("write agent");
}
fn write_override(project: &Path, name: &str, content: &str) {
let dir = project.join(OVERRIDE_DIR_NAME);
fs::create_dir_all(&dir).expect("create .trusty-mpm");
fs::write(dir.join(name), content).expect("write override");
}
fn project_with_roster() -> TempDir {
let tmp = TempDir::new().expect("tempdir");
deploy_agent(tmp.path(), "ticketing");
tmp
}
#[test]
fn resolve_pm_prompt_takes_the_package_path_when_a_roster_is_deployed() {
let tmp = project_with_roster();
let (_, source) = resolve_pm_prompt_with_source(tmp.path());
assert_eq!(
source,
PromptSource::Package,
"a deployed roster with no section override must compose via InstructionPackage"
);
}
fn assert_entry_point_gate(project: &Path, addendum: Option<&str>) {
let (composed, source) =
resolve_pm_prompt_with_roster(project, || Some(FIXED_ROSTER.to_string()));
assert_eq!(
source,
PromptSource::Package,
"the gate must run against the COMPOSED path, or it proves nothing"
);
let legacy = legacy_bundled_fallback(&stack_profile_section(project), FIXED_ROSTER, addendum);
assert_byte_identical(&legacy, &composed);
}
#[test]
fn resolve_pm_prompt_is_byte_identical_to_the_legacy_assembly() {
let tmp = TempDir::new().expect("tempdir");
assert_entry_point_gate(tmp.path(), None);
}
#[test]
fn a_retired_instructions_file_cannot_feed_the_project_addendum() {
let tmp = TempDir::new().expect("tempdir");
write_override(
tmp.path(),
FILE_INSTRUCTIONS,
"# Project Rules\n\nALWAYS_RUN_MAKE_CHECK\n",
);
let (composed, _) =
resolve_pm_prompt_with_roster(tmp.path(), || Some(FIXED_ROSTER.to_string()));
assert!(
!composed.contains("ALWAYS_RUN_MAKE_CHECK"),
"a retired INSTRUCTIONS.md must not feed the project addendum"
);
assert_entry_point_gate(tmp.path(), None);
}
#[test]
fn gate_is_deterministic_across_repeated_runs() {
let tmp = TempDir::new().expect("tempdir");
let first = resolve_pm_prompt_with_roster(tmp.path(), || Some(FIXED_ROSTER.to_string())).0;
for _ in 0..32 {
assert_entry_point_gate(tmp.path(), None);
let again = resolve_pm_prompt_with_roster(tmp.path(), || Some(FIXED_ROSTER.to_string())).0;
assert_byte_identical(&first, &again);
}
}
#[test]
fn the_roster_alone_selects_the_composer() {
let tmp = TempDir::new().expect("tempdir");
let (_, source) = resolve_pm_prompt_with_roster(tmp.path(), || Some(FIXED_ROSTER.to_string()));
assert_eq!(source, PromptSource::Package);
let (_, source) = resolve_pm_prompt_with_roster(tmp.path(), || None);
assert_eq!(source, PromptSource::Legacy);
}
#[test]
fn no_retired_file_can_divert_the_composer() {
for name in crate::core::instruction_overrides::LEGACY_OVERRIDE_FILES {
let tmp = TempDir::new().expect("tempdir");
write_override(tmp.path(), name, "# Retired\n\nX\n");
let (_, source) =
resolve_pm_prompt_with_roster(tmp.path(), || Some(FIXED_ROSTER.to_string()));
assert_eq!(
source,
PromptSource::Package,
"{name} must not divert the composer"
);
}
let all = TempDir::new().expect("tempdir");
for name in crate::core::instruction_overrides::LEGACY_OVERRIDE_FILES {
write_override(all.path(), name, "# Retired\n\nX\n");
}
let (_, source) = resolve_pm_prompt_with_roster(all.path(), || Some(FIXED_ROSTER.to_string()));
assert_eq!(source, PromptSource::Package);
}
#[test]
fn the_roster_source_is_consulted_exactly_once() {
use std::cell::Cell;
let scans = Cell::new(0usize);
let plain = TempDir::new().expect("tempdir");
let _ = resolve_pm_prompt_with_roster(plain.path(), || {
scans.set(scans.get() + 1);
Some(FIXED_ROSTER.to_string())
});
assert_eq!(scans.get(), 1, "the bundled fallback scans exactly once");
let retired = TempDir::new().expect("tempdir");
write_override(
retired.path(),
FILE_AGENT_DELEGATION,
"# Custom Routing\n\nX\n",
);
let _ = resolve_pm_prompt_with_roster(retired.path(), || {
scans.set(scans.get() + 1);
Some(FIXED_ROSTER.to_string())
});
assert_eq!(scans.get(), 2, "a retired file must not suppress the scan");
}
#[test]
fn resolve_pm_prompt_wrapper_matches_the_source_reporting_form() {
let tmp = TempDir::new().expect("tempdir");
let (with_source, _) = resolve_pm_prompt_with_source(tmp.path());
assert_byte_identical(&resolve_pm_prompt(tmp.path()), &with_source);
}
#[test]
fn roster_is_required_and_never_droppable() {
let package = package_ref();
assert_ne!(package.validate(), Err(ValidationError::RosterNotConsumed));
let roster_block = package
.blocks
.iter()
.find(|b| {
matches!(
b.body,
BlockBody::Generated {
generator: Generator::AgentRoster
}
)
})
.expect("a block consumes the roster");
assert!(
!roster_block.optional,
"the roster block must not be optional"
);
let err = compose_bundled_fallback(FIXED_STACK, " \n\t", None)
.expect_err("an empty roster must not compose");
assert!(
matches!(
err,
CompositionError::MissingGeneratedInput {
generator: Generator::AgentRoster,
..
}
),
"expected a missing-roster composition error, got {err:?}"
);
}
#[test]
fn composed_prompt_carries_the_live_roster_and_the_precedence_note() {
let composed =
compose_bundled_fallback(FIXED_STACK, FIXED_ROSTER, None).expect("package composes");
let doctrine = composed
.find("# Agent Delegation Routing")
.expect("doctrine");
let note = composed.find("trust the roster").expect("note");
let roster = composed.find("### ticketing").expect("roster");
let floor = composed.find("# Framework Instructions").expect("floor");
assert!(doctrine < note && note < roster && roster < floor);
}
#[test]
fn empty_stack_profile_is_dropped_without_a_dangling_rule() {
let composed = compose_bundled_fallback("", FIXED_ROSTER, None).expect("package composes");
let legacy = legacy_bundled_fallback("", FIXED_ROSTER, None);
assert_byte_identical(&legacy, &composed);
assert!(!composed.contains("\n\n---\n\n---\n\n"));
}
#[test]
fn every_authored_block_is_exactly_its_section_source() {
let package = package_ref();
let expected: Vec<(SectionId, &str)> = vec![
(SectionId::Core, SECTION_CORE),
(SectionId::Memory, SECTION_MEMORY),
(SectionId::Search, SECTION_SEARCH),
(SectionId::Workflow, WORKFLOW),
(SectionId::AgentDelegation, AGENT_DELEGATION),
(SectionId::Identity, SECTION_IDENTITY),
(SectionId::Enforcement, SECTION_ENFORCEMENT),
(
SectionId::NonOverridableRules,
SECTION_NON_OVERRIDABLE_RULES,
),
(
SectionId::FrameworkGuaranteedConventions,
SECTION_FRAMEWORK_CONVENTIONS,
),
];
for (section, source) in expected {
let found = package
.blocks
.iter()
.any(|b| b.section == section && authored(b) == Some(source));
assert!(
found,
"section {section:?} must contribute its source file verbatim"
);
}
}
#[test]
fn memory_and_search_are_independently_overridable() {
let package = package_ref();
for section in [SectionId::Memory, SectionId::Search] {
let text = package
.blocks
.iter()
.filter(|b| b.section == section)
.find_map(authored)
.expect("section contributes an authored block")
.trim();
assert!(
text.starts_with("## "),
"{section:?} must open with its own heading, got {:?}",
&text[..text.len().min(40)]
);
assert!(
!text.contains("Both tools are"),
"{section:?} must not speak for the other section"
);
}
}
#[test]
fn floor_carries_the_tool_priority_mandate() {
let package = package_ref();
let rules = package
.blocks
.iter()
.find(|b| b.section == SectionId::NonOverridableRules)
.expect("the rules section contributes a block");
let text = authored(rules).expect("the rules block must be authored, not generated");
assert!(text.contains("## Trusty Tool Priority (Non-Overridable)"));
assert!(text.contains("mcp__trusty-search__search"));
}
#[test]
fn floor_sections_refuse_every_override_tier() {
let package = package_ref();
for id in SectionId::CANONICAL
.into_iter()
.filter(|id| *id == SectionId::Core)
{
let tier = package.section(id).expect("declared").customization_tier;
assert_eq!(tier, CustomizationTier::Fixed, "{id:?} must be fixed");
for from in [OverrideTier::Project, OverrideTier::User] {
assert!(
!tier.permits(from),
"{id:?} is fixed and must refuse a {from:?}-tier override"
);
}
}
}
#[test]
fn content_sections_admit_project_but_not_user_overrides() {
let package = package_ref();
for id in SectionId::CANONICAL
.into_iter()
.filter(|id| *id != SectionId::Core)
{
let tier = package.section(id).expect("declared").customization_tier;
assert_eq!(tier, CustomizationTier::Project, "{id:?} must be project");
assert!(tier.permits(OverrideTier::Project), "{id:?}");
assert!(
!tier.permits(OverrideTier::User),
"{id:?} must not admit a user-tier override"
);
}
}
#[test]
fn every_advertised_override_file_maps_to_an_overridable_section() {
let package = package_ref();
let floor = package
.blocks
.iter()
.filter(|b| b.section == SectionId::NonOverridableRules)
.find_map(authored)
.expect("rules block");
for (file, section) in [
(FILE_WORKFLOW, SectionId::Workflow),
(FILE_AGENT_DELEGATION, SectionId::AgentDelegation),
(FILE_MEMORY, SectionId::Memory),
] {
assert!(
floor.contains(&format!(".trusty-mpm/{file}")),
"{file} must stay advertised in the customization table"
);
assert!(
package
.section(section)
.expect("declared")
.customization_tier
.permits(OverrideTier::Project),
"{file} is advertised, so {section:?} must admit a project override"
);
}
}
#[test]
fn the_delivered_prompt_teaches_sprint_then_harden() {
let composed =
compose_bundled_fallback(FIXED_STACK, FIXED_ROSTER, None).expect("package composes");
for marker in [
"## Sprint, then Harden",
"feature-complete on a local version",
"no CI iteration loops",
"no critic round on narrow changes",
"full suite, critic, release gates",
"Publish only after that",
"*causes* too many things in flight",
"never turn red green by deleting coverage",
"3+ review rounds is evidence to close and fold",
] {
assert!(
composed.contains(marker),
"the delivered prompt must carry the sprint/harden doctrine: {marker:?}"
);
}
}