use crate::analysis::ClassifiedSeam;
use crate::analysis::seams::SeamGripClass;
use crate::analysis::test_grip_evidence::{RelatedTestGrip, RelationConfidence};
use crate::config::{ConfigSeverity, RiprConfig};
use std::cmp::Ordering;
use std::path::{Path, PathBuf};
pub(crate) const DEFAULT_AGENT_BRIEF_MAX_SEAMS: usize = 3;
pub(crate) const AGENT_BRIEF_HARD_MAX_SEAMS: usize = 10;
const RELATED_TEST_ASSERTION_WINDOW_LINES: usize = 12;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum AgentBriefWorkingSetSource {
Diff,
Base,
Files,
SeamId,
}
impl AgentBriefWorkingSetSource {
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::Diff => "diff",
Self::Base => "base",
Self::Files => "files",
Self::SeamId => "seam_id",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct AgentBriefLine {
pub(crate) file: PathBuf,
pub(crate) line: usize,
}
impl AgentBriefLine {
pub(crate) fn new(file: impl Into<PathBuf>, line: usize) -> Self {
Self {
file: file.into(),
line,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct AgentBriefChangedOwner {
pub(crate) file: PathBuf,
pub(crate) line: usize,
pub(crate) owner: String,
}
impl AgentBriefChangedOwner {
pub(crate) fn new(file: impl Into<PathBuf>, line: usize, owner: impl Into<String>) -> Self {
Self {
file: file.into(),
line,
owner: owner.into(),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct AgentBriefResolvedWorkingSet {
pub(crate) source: AgentBriefWorkingSetSource,
pub(crate) files: Vec<PathBuf>,
pub(crate) changed_lines: Vec<AgentBriefLine>,
pub(crate) changed_owners: Vec<AgentBriefChangedOwner>,
pub(crate) base: Option<String>,
pub(crate) diff: Option<PathBuf>,
pub(crate) seam_id: Option<String>,
}
impl AgentBriefResolvedWorkingSet {
pub(crate) fn diff(diff: impl Into<PathBuf>, changed_lines: Vec<AgentBriefLine>) -> Self {
let files = files_from_changed_lines(&changed_lines);
Self {
source: AgentBriefWorkingSetSource::Diff,
files,
changed_lines,
changed_owners: Vec::new(),
base: None,
diff: Some(diff.into()),
seam_id: None,
}
}
pub(crate) fn base(base: impl Into<String>, changed_lines: Vec<AgentBriefLine>) -> Self {
let files = files_from_changed_lines(&changed_lines);
Self {
source: AgentBriefWorkingSetSource::Base,
files,
changed_lines,
changed_owners: Vec::new(),
base: Some(base.into()),
diff: None,
seam_id: None,
}
}
pub(crate) fn files(files: Vec<PathBuf>) -> Self {
Self {
source: AgentBriefWorkingSetSource::Files,
files,
changed_lines: Vec::new(),
changed_owners: Vec::new(),
base: None,
diff: None,
seam_id: None,
}
}
pub(crate) fn seam_id(seam_id: impl Into<String>) -> Self {
Self {
source: AgentBriefWorkingSetSource::SeamId,
files: Vec::new(),
changed_lines: Vec::new(),
changed_owners: Vec::new(),
base: None,
diff: None,
seam_id: Some(seam_id.into()),
}
}
pub(crate) fn with_changed_owners(mut self, owners: Vec<AgentBriefChangedOwner>) -> Self {
self.changed_owners = owners;
self
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum AgentBriefWhyNowReason {
ChangedLineIntersectsSeam,
ChangedOwnerFunction,
ChangedTestForRelatedSeam,
ChangedAssertionNearRelatedTest,
SameFileSeam,
ExplicitSeamId,
RepoActionableFallback,
}
impl AgentBriefWhyNowReason {
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::ChangedLineIntersectsSeam => "changed_line_intersects_seam",
Self::ChangedOwnerFunction => "changed_owner_function",
Self::ChangedTestForRelatedSeam => "changed_test_for_related_seam",
Self::ChangedAssertionNearRelatedTest => "changed_assertion_near_related_test",
Self::SameFileSeam => "same_file_seam",
Self::ExplicitSeamId => "explicit_seam_id",
Self::RepoActionableFallback => "repo_actionable_fallback",
}
}
}
const AGENT_BRIEF_WHY_NOW_REASON_VOCABULARY: [AgentBriefWhyNowReason; 7] = [
AgentBriefWhyNowReason::ChangedLineIntersectsSeam,
AgentBriefWhyNowReason::ChangedOwnerFunction,
AgentBriefWhyNowReason::ChangedTestForRelatedSeam,
AgentBriefWhyNowReason::ChangedAssertionNearRelatedTest,
AgentBriefWhyNowReason::SameFileSeam,
AgentBriefWhyNowReason::ExplicitSeamId,
AgentBriefWhyNowReason::RepoActionableFallback,
];
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum AgentBriefWhyNowConfidence {
High,
Medium,
Low,
Unknown,
}
impl AgentBriefWhyNowConfidence {
pub(crate) fn as_str(self) -> &'static str {
match self {
Self::High => "high",
Self::Medium => "medium",
Self::Low => "low",
Self::Unknown => "unknown",
}
}
}
const AGENT_BRIEF_WHY_NOW_CONFIDENCE_VOCABULARY: [AgentBriefWhyNowConfidence; 4] = [
AgentBriefWhyNowConfidence::High,
AgentBriefWhyNowConfidence::Medium,
AgentBriefWhyNowConfidence::Low,
AgentBriefWhyNowConfidence::Unknown,
];
#[derive(Clone, Debug, PartialEq, Eq)]
pub(crate) struct AgentBriefWhyNow {
pub(crate) reason: AgentBriefWhyNowReason,
pub(crate) confidence: AgentBriefWhyNowConfidence,
pub(crate) evidence: String,
}
#[derive(Clone, Debug)]
pub(crate) struct AgentBriefSelectedSeam<'a> {
pub(crate) seam: &'a ClassifiedSeam,
pub(crate) why_now: AgentBriefWhyNow,
}
#[derive(Clone, Debug)]
pub(crate) struct AgentBriefSelection<'a> {
pub(crate) requested: usize,
pub(crate) returned: usize,
pub(crate) default: usize,
pub(crate) hard_cap: usize,
pub(crate) top_seams: Vec<AgentBriefSelectedSeam<'a>>,
pub(crate) warnings: Vec<String>,
}
#[derive(Clone, Copy, Debug)]
pub(crate) struct AgentBriefPolicy<'a> {
config: &'a RiprConfig,
}
impl<'a> AgentBriefPolicy<'a> {
pub(crate) fn from_config(config: &'a RiprConfig) -> Self {
Self { config }
}
fn severity_for(self, class: SeamGripClass) -> ConfigSeverity {
self.config.severity().for_seam(class)
}
pub(crate) fn omission_reason_for_class(self, class: SeamGripClass) -> Option<String> {
if matches!(self.severity_for(class), ConfigSeverity::Off) {
return Some(format!(
"is configured off for {} seams and is not included in agent results",
class.as_str()
));
}
if !is_agent_actionable(class) {
return Some(format!(
"is {} and is not included in agent results",
class.as_str()
));
}
None
}
}
pub(crate) fn select_agent_brief_seams<'a>(
classified: &'a [ClassifiedSeam],
working_set: &AgentBriefResolvedWorkingSet,
requested_max: usize,
policy: AgentBriefPolicy<'_>,
) -> AgentBriefSelection<'a> {
debug_assert_eq!(AGENT_BRIEF_WHY_NOW_REASON_VOCABULARY.len(), 7);
debug_assert_eq!(AGENT_BRIEF_WHY_NOW_CONFIDENCE_VOCABULARY.len(), 4);
let max_seams = normalize_requested_max(requested_max);
let mut warnings = Vec::new();
let mut direct = direct_candidates(classified, working_set, policy, &mut warnings);
if direct.candidates.is_empty() && direct.allow_fallback {
direct.candidates = fallback_candidates(classified, policy);
}
direct
.candidates
.sort_by(|left, right| compare_selected(left, right, policy));
let omitted_by_cap = direct.candidates.len().saturating_sub(max_seams);
if omitted_by_cap > 0 {
warnings.push(format!(
"{omitted_by_cap} additional visible seams were omitted by the brief cap"
));
}
direct.candidates.truncate(max_seams);
let returned = direct.candidates.len();
AgentBriefSelection {
requested: requested_max,
returned,
default: DEFAULT_AGENT_BRIEF_MAX_SEAMS,
hard_cap: AGENT_BRIEF_HARD_MAX_SEAMS,
top_seams: direct.candidates,
warnings,
}
}
fn normalize_requested_max(requested: usize) -> usize {
if requested == 0 {
DEFAULT_AGENT_BRIEF_MAX_SEAMS
} else {
requested.min(AGENT_BRIEF_HARD_MAX_SEAMS)
}
}
struct AgentBriefCandidateSelection<'a> {
candidates: Vec<AgentBriefSelectedSeam<'a>>,
allow_fallback: bool,
}
fn direct_candidates<'a>(
classified: &'a [ClassifiedSeam],
working_set: &AgentBriefResolvedWorkingSet,
policy: AgentBriefPolicy<'_>,
warnings: &mut Vec<String>,
) -> AgentBriefCandidateSelection<'a> {
if let Some(seam_id) = working_set.seam_id.as_deref() {
return AgentBriefCandidateSelection {
candidates: explicit_seam_candidate(classified, seam_id, policy, warnings),
allow_fallback: false,
};
}
let mut candidates = Vec::new();
let mut matched_working_set = false;
let normalized_working_set = NormalizedAgentBriefWorkingSet::new(working_set);
for entry in classified {
let Some(why_now) = why_now_for(entry, &normalized_working_set) else {
continue;
};
matched_working_set = true;
if let Some(reason) = agent_brief_omission_reason(entry, policy) {
warnings.push(format!(
"seam {} at {}:{} {reason}",
entry.seam.id().as_str(),
display_path(entry.seam.file()),
entry.seam.display_line()
));
continue;
}
candidates.push(selected(entry, why_now));
}
AgentBriefCandidateSelection {
candidates,
allow_fallback: !matched_working_set,
}
}
fn explicit_seam_candidate<'a>(
classified: &'a [ClassifiedSeam],
seam_id: &str,
policy: AgentBriefPolicy<'_>,
warnings: &mut Vec<String>,
) -> Vec<AgentBriefSelectedSeam<'a>> {
let Some(entry) = classified
.iter()
.find(|entry| entry.seam.id().as_str() == seam_id)
else {
warnings.push(format!("requested seam_id {seam_id} was not found"));
return Vec::new();
};
if let Some(reason) = agent_brief_omission_reason(entry, policy) {
warnings.push(format!("requested seam_id {seam_id} {reason}"));
return Vec::new();
}
vec![selected(
entry,
AgentBriefWhyNow {
reason: AgentBriefWhyNowReason::ExplicitSeamId,
confidence: AgentBriefWhyNowConfidence::High,
evidence: format!("caller requested seam_id {seam_id}"),
},
)]
}
fn fallback_candidates<'a>(
classified: &'a [ClassifiedSeam],
policy: AgentBriefPolicy<'_>,
) -> Vec<AgentBriefSelectedSeam<'a>> {
classified
.iter()
.filter(|entry| agent_brief_omission_reason(entry, policy).is_none())
.map(|entry| {
selected(
entry,
AgentBriefWhyNow {
reason: AgentBriefWhyNowReason::RepoActionableFallback,
confidence: AgentBriefWhyNowConfidence::Low,
evidence: "no working-set seam matched; selected a repo-actionable seam"
.to_string(),
},
)
})
.collect()
}
struct NormalizedAgentBriefWorkingSet<'a> {
changed_lines: Vec<(String, &'a AgentBriefLine)>,
changed_owners: Vec<(String, &'a AgentBriefChangedOwner)>,
files: Vec<String>,
}
impl<'a> NormalizedAgentBriefWorkingSet<'a> {
fn new(working_set: &'a AgentBriefResolvedWorkingSet) -> Self {
let changed_lines = working_set
.changed_lines
.iter()
.map(|line| (normalized_path(&line.file), line))
.collect();
let files = working_set
.files
.iter()
.map(|file| normalized_path(file))
.collect();
let changed_owners = working_set
.changed_owners
.iter()
.map(|owner| (normalized_path(&owner.file), owner))
.collect();
Self {
changed_lines,
changed_owners,
files,
}
}
}
fn why_now_for(
entry: &ClassifiedSeam,
working_set: &NormalizedAgentBriefWorkingSet<'_>,
) -> Option<AgentBriefWhyNow> {
if let Some(line) = matching_changed_line(entry, working_set) {
return Some(AgentBriefWhyNow {
reason: AgentBriefWhyNowReason::ChangedLineIntersectsSeam,
confidence: AgentBriefWhyNowConfidence::High,
evidence: format!("changed line {} intersects the seam origin", line.line),
});
}
if let Some((test, line)) = matching_related_test_exact_line(entry, working_set) {
return Some(AgentBriefWhyNow {
reason: AgentBriefWhyNowReason::ChangedTestForRelatedSeam,
confidence: AgentBriefWhyNowConfidence::High,
evidence: format!(
"changed line {} intersects related test {} at {}:{}",
line.line,
test.test_name,
display_path(&test.file),
test.line
),
});
}
if let Some(owner) = matching_changed_owner(entry, working_set) {
return Some(AgentBriefWhyNow {
reason: AgentBriefWhyNowReason::ChangedOwnerFunction,
confidence: AgentBriefWhyNowConfidence::High,
evidence: format!(
"changed line {} is inside owner {}",
owner.line, owner.owner
),
});
}
if let Some((test, line)) = matching_related_test_assertion_line(entry, working_set) {
return Some(AgentBriefWhyNow {
reason: AgentBriefWhyNowReason::ChangedAssertionNearRelatedTest,
confidence: AgentBriefWhyNowConfidence::Medium,
evidence: format!(
"changed line {} is near related test {} at {}:{}",
line.line,
test.test_name,
display_path(&test.file),
test.line
),
});
}
if let Some(test) = matching_related_test_file(entry, working_set) {
return Some(AgentBriefWhyNow {
reason: AgentBriefWhyNowReason::ChangedTestForRelatedSeam,
confidence: AgentBriefWhyNowConfidence::Medium,
evidence: format!(
"working set includes related test {} at {}:{}",
test.test_name,
display_path(&test.file),
test.line
),
});
}
let seam_file = normalized_path(entry.seam.file());
if working_set.files.iter().any(|file| file == &seam_file) {
return Some(AgentBriefWhyNow {
reason: AgentBriefWhyNowReason::SameFileSeam,
confidence: AgentBriefWhyNowConfidence::Medium,
evidence: format!("working set includes {}", display_path(entry.seam.file())),
});
}
None
}
fn matching_changed_line<'a>(
entry: &ClassifiedSeam,
working_set: &'a NormalizedAgentBriefWorkingSet<'a>,
) -> Option<&'a AgentBriefLine> {
let seam_file = normalized_path(entry.seam.file());
working_set
.changed_lines
.iter()
.find_map(|(line_file, line)| {
(line_file == &seam_file && line.line == entry.seam.display_line()).then_some(*line)
})
}
fn matching_changed_owner<'a>(
entry: &ClassifiedSeam,
working_set: &'a NormalizedAgentBriefWorkingSet<'a>,
) -> Option<&'a AgentBriefChangedOwner> {
let seam_file = normalized_path(entry.seam.file());
working_set
.changed_owners
.iter()
.find_map(|(owner_file, owner)| {
(owner_file == &seam_file && owner.owner == entry.seam.owner()).then_some(*owner)
})
}
fn matching_related_test_exact_line<'a>(
entry: &'a ClassifiedSeam,
working_set: &'a NormalizedAgentBriefWorkingSet<'a>,
) -> Option<(&'a RelatedTestGrip, &'a AgentBriefLine)> {
matching_related_test_line(entry, working_set, |test, line| line.line == test.line)
}
fn matching_related_test_assertion_line<'a>(
entry: &'a ClassifiedSeam,
working_set: &'a NormalizedAgentBriefWorkingSet<'a>,
) -> Option<(&'a RelatedTestGrip, &'a AgentBriefLine)> {
matching_related_test_line(entry, working_set, |test, line| {
line.line > test.line
&& line
.line
.checked_sub(test.line)
.is_some_and(|distance| distance <= RELATED_TEST_ASSERTION_WINDOW_LINES)
})
}
fn matching_related_test_line<'a>(
entry: &'a ClassifiedSeam,
working_set: &'a NormalizedAgentBriefWorkingSet<'a>,
matches_line: impl Fn(&RelatedTestGrip, &AgentBriefLine) -> bool,
) -> Option<(&'a RelatedTestGrip, &'a AgentBriefLine)> {
if entry.evidence.related_tests.is_empty() {
return None;
}
entry.evidence.related_tests.iter().find_map(|test| {
let test_file = normalized_path(&test.file);
working_set
.changed_lines
.iter()
.find_map(|(line_file, line)| {
(line_file == &test_file && matches_line(test, line)).then_some((test, *line))
})
})
}
fn matching_related_test_file<'a>(
entry: &'a ClassifiedSeam,
working_set: &NormalizedAgentBriefWorkingSet<'_>,
) -> Option<&'a RelatedTestGrip> {
if entry.evidence.related_tests.is_empty() {
return None;
}
entry.evidence.related_tests.iter().find(|test| {
let test_file = normalized_path(&test.file);
working_set.files.iter().any(|file| file == &test_file)
})
}
fn selected<'a>(seam: &'a ClassifiedSeam, why_now: AgentBriefWhyNow) -> AgentBriefSelectedSeam<'a> {
AgentBriefSelectedSeam { seam, why_now }
}
fn compare_selected(
left: &AgentBriefSelectedSeam<'_>,
right: &AgentBriefSelectedSeam<'_>,
policy: AgentBriefPolicy<'_>,
) -> Ordering {
why_now_sort_priority(&left.why_now)
.cmp(&why_now_sort_priority(&right.why_now))
.then_with(|| {
severity_priority(policy.severity_for(left.seam.class))
.cmp(&severity_priority(policy.severity_for(right.seam.class)))
})
.then_with(|| grip_priority(left.seam.class).cmp(&grip_priority(right.seam.class)))
.then_with(|| {
best_relation_confidence_priority(left.seam)
.cmp(&best_relation_confidence_priority(right.seam))
})
.then_with(|| {
normalized_path(left.seam.seam.file()).cmp(&normalized_path(right.seam.seam.file()))
})
.then_with(|| {
left.seam
.seam
.display_line()
.cmp(&right.seam.seam.display_line())
})
.then_with(|| {
left.seam
.seam
.id()
.as_str()
.cmp(right.seam.seam.id().as_str())
})
}
fn why_now_sort_priority(why_now: &AgentBriefWhyNow) -> u8 {
match (why_now.reason, why_now.confidence) {
(AgentBriefWhyNowReason::ExplicitSeamId, _) => 0,
(AgentBriefWhyNowReason::ChangedLineIntersectsSeam, _) => 1,
(AgentBriefWhyNowReason::ChangedOwnerFunction, _) => 2,
(AgentBriefWhyNowReason::ChangedTestForRelatedSeam, AgentBriefWhyNowConfidence::High) => 3,
(AgentBriefWhyNowReason::ChangedAssertionNearRelatedTest, _) => 4,
(AgentBriefWhyNowReason::ChangedTestForRelatedSeam, _) => 5,
(AgentBriefWhyNowReason::SameFileSeam, _) => 6,
(AgentBriefWhyNowReason::RepoActionableFallback, _) => 7,
}
}
fn grip_priority(class: SeamGripClass) -> u8 {
match class {
SeamGripClass::WeaklyGripped => 0,
SeamGripClass::Ungripped => 1,
SeamGripClass::ReachableUnrevealed => 2,
SeamGripClass::ActivationUnknown => 3,
SeamGripClass::PropagationUnknown => 4,
SeamGripClass::ObservationUnknown => 5,
SeamGripClass::DiscriminationUnknown => 6,
SeamGripClass::Opaque => 7,
SeamGripClass::StronglyGripped => 8,
SeamGripClass::Intentional => 9,
SeamGripClass::Suppressed => 10,
}
}
fn severity_priority(severity: ConfigSeverity) -> u8 {
match severity {
ConfigSeverity::Warning => 0,
ConfigSeverity::Info => 1,
ConfigSeverity::Note => 2,
ConfigSeverity::Off => 3,
}
}
fn best_relation_confidence_priority(entry: &ClassifiedSeam) -> u8 {
entry
.evidence
.related_tests
.iter()
.map(|test| relation_confidence_priority(test.relation_confidence))
.min()
.unwrap_or(4)
}
fn relation_confidence_priority(confidence: RelationConfidence) -> u8 {
match confidence {
RelationConfidence::High => 0,
RelationConfidence::Medium => 1,
RelationConfidence::Low => 2,
RelationConfidence::Opaque => 3,
}
}
fn is_agent_actionable(class: SeamGripClass) -> bool {
class.is_headline_eligible() || matches!(class, SeamGripClass::Opaque)
}
fn agent_brief_omission_reason(
entry: &ClassifiedSeam,
policy: AgentBriefPolicy<'_>,
) -> Option<String> {
if matches!(policy.severity_for(entry.class), ConfigSeverity::Off) {
return Some(format!(
"is configured off for {} seams and is not included in agent brief results",
entry.class.as_str()
));
}
if !is_agent_actionable(entry.class) {
return Some(format!(
"is {} and is not included in agent brief results",
entry.class.as_str()
));
}
None
}
fn files_from_changed_lines(changed_lines: &[AgentBriefLine]) -> Vec<PathBuf> {
let mut files: Vec<PathBuf> = Vec::new();
for line in changed_lines {
if !files.iter().any(|file| same_file(file, &line.file)) {
files.push(line.file.clone());
}
}
files
}
fn same_file(left: &Path, right: &Path) -> bool {
normalized_path(left) == normalized_path(right)
}
fn normalized_path(path: &Path) -> String {
let normalized = path.to_string_lossy().replace('\\', "/");
normalized
.strip_prefix("./")
.unwrap_or(&normalized)
.to_string()
}
fn display_path(path: &Path) -> String {
normalized_path(path)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::analysis::seams::{ExpectedSink, RepoSeam, RequiredDiscriminator, SeamKind};
use crate::analysis::test_grip_evidence::{
RelatedTestGrip, RelationConfidence, RelationReason, TestGripEvidence,
};
use crate::config::{RiprConfig, tests_only_parse};
use crate::domain::{
Confidence, MissingDiscriminatorFact, OracleKind, OracleStrength, StageEvidence,
StageState, ValueFact,
};
fn stage(state: StageState) -> StageEvidence {
StageEvidence::new(state, Confidence::Medium, "test stage")
}
fn classified(
file: &str,
line: usize,
owner: &str,
expression: &str,
class: SeamGripClass,
) -> ClassifiedSeam {
let seam = RepoSeam::new(
file,
owner,
SeamKind::PredicateBoundary,
line * 10,
line,
expression,
RequiredDiscriminator::BoundaryValue {
description: expression.to_string(),
},
ExpectedSink::ReturnValue,
);
let seam_id = seam.id().clone();
ClassifiedSeam {
seam,
class,
evidence: TestGripEvidence {
seam_id,
related_tests: vec![RelatedTestGrip {
test_name: format!("{owner}_test"),
file: PathBuf::from("tests/sample.rs"),
line: 5,
oracle_kind: OracleKind::ExactValue,
oracle_strength: OracleStrength::Strong,
evidence_summary: "exact value assertion".to_string(),
relation_reason: RelationReason::DirectOwnerCall,
relation_confidence: RelationConfidence::High,
}],
reach: stage(StageState::Yes),
activate: stage(StageState::Yes),
propagate: stage(StageState::Yes),
observe: stage(StageState::Yes),
discriminate: stage(StageState::Weak),
observed_values: Vec::<ValueFact>::new(),
missing_discriminators: vec![MissingDiscriminatorFact {
value: "boundary value".to_string(),
reason: "missing equality boundary".to_string(),
flow_sink: None,
}],
},
}
}
fn related_test(
entry: &mut ClassifiedSeam,
file: &str,
line: usize,
confidence: RelationConfidence,
) {
entry.evidence.related_tests[0].file = PathBuf::from(file);
entry.evidence.related_tests[0].line = line;
entry.evidence.related_tests[0].relation_confidence = confidence;
}
fn selected_ids(selection: &AgentBriefSelection<'_>) -> Vec<String> {
selection
.top_seams
.iter()
.map(|entry| entry.seam.seam.id().as_str().to_string())
.collect()
}
fn select<'a>(
seams: &'a [ClassifiedSeam],
working_set: &AgentBriefResolvedWorkingSet,
requested_max: usize,
) -> AgentBriefSelection<'a> {
let config = RiprConfig::default();
select_agent_brief_seams(
seams,
working_set,
requested_max,
AgentBriefPolicy::from_config(&config),
)
}
fn select_with_config<'a>(
seams: &'a [ClassifiedSeam],
working_set: &AgentBriefResolvedWorkingSet,
requested_max: usize,
config: &RiprConfig,
) -> AgentBriefSelection<'a> {
select_agent_brief_seams(
seams,
working_set,
requested_max,
AgentBriefPolicy::from_config(config),
)
}
#[test]
fn agent_brief_why_now_vocabulary_matches_spec_contract() {
assert_eq!(AgentBriefWorkingSetSource::Diff.as_str(), "diff");
assert_eq!(AgentBriefWorkingSetSource::Base.as_str(), "base");
assert_eq!(AgentBriefWorkingSetSource::Files.as_str(), "files");
assert_eq!(AgentBriefWorkingSetSource::SeamId.as_str(), "seam_id");
assert_eq!(
AGENT_BRIEF_WHY_NOW_REASON_VOCABULARY.map(AgentBriefWhyNowReason::as_str),
[
"changed_line_intersects_seam",
"changed_owner_function",
"changed_test_for_related_seam",
"changed_assertion_near_related_test",
"same_file_seam",
"explicit_seam_id",
"repo_actionable_fallback",
]
);
let priority_cases = [
(
AgentBriefWhyNowReason::ChangedLineIntersectsSeam,
AgentBriefWhyNowConfidence::High,
1,
),
(
AgentBriefWhyNowReason::ChangedOwnerFunction,
AgentBriefWhyNowConfidence::High,
2,
),
(
AgentBriefWhyNowReason::ChangedTestForRelatedSeam,
AgentBriefWhyNowConfidence::High,
3,
),
(
AgentBriefWhyNowReason::ChangedAssertionNearRelatedTest,
AgentBriefWhyNowConfidence::Medium,
4,
),
(
AgentBriefWhyNowReason::ChangedTestForRelatedSeam,
AgentBriefWhyNowConfidence::Medium,
5,
),
(
AgentBriefWhyNowReason::SameFileSeam,
AgentBriefWhyNowConfidence::Medium,
6,
),
(
AgentBriefWhyNowReason::ExplicitSeamId,
AgentBriefWhyNowConfidence::High,
0,
),
(
AgentBriefWhyNowReason::RepoActionableFallback,
AgentBriefWhyNowConfidence::Low,
7,
),
];
for (reason, confidence, expected) in priority_cases {
assert_eq!(
why_now_sort_priority(&AgentBriefWhyNow {
reason,
confidence,
evidence: String::new(),
}),
expected
);
}
assert_eq!(
AGENT_BRIEF_WHY_NOW_CONFIDENCE_VOCABULARY.map(AgentBriefWhyNowConfidence::as_str),
["high", "medium", "low", "unknown"]
);
}
#[test]
fn agent_brief_base_working_set_derives_files_from_changed_lines() {
let working_set = AgentBriefResolvedWorkingSet::base(
"main",
vec![
AgentBriefLine::new("src/pricing.rs", 88),
AgentBriefLine::new("src/pricing.rs", 89),
AgentBriefLine::new("src/tax.rs", 12),
],
);
assert_eq!(working_set.source, AgentBriefWorkingSetSource::Base);
assert_eq!(working_set.base.as_deref(), Some("main"));
assert_eq!(working_set.diff, None);
assert_eq!(working_set.seam_id, None);
assert_eq!(
working_set.files,
vec![PathBuf::from("src/pricing.rs"), PathBuf::from("src/tax.rs")]
);
}
#[test]
fn agent_brief_selector_ranks_explicit_seam_id_first() {
let unrelated = classified(
"src/pricing.rs",
12,
"pricing::unrelated",
"amount > 0",
SeamGripClass::WeaklyGripped,
);
let requested = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::ReachableUnrevealed,
);
let seam_id = requested.seam.id().as_str().to_string();
let seams = vec![unrelated, requested];
let selection = select(&seams, &AgentBriefResolvedWorkingSet::seam_id(&seam_id), 3);
assert_eq!(selected_ids(&selection), vec![seam_id]);
assert_eq!(
selection.top_seams[0].why_now.reason,
AgentBriefWhyNowReason::ExplicitSeamId
);
assert_eq!(
selection.top_seams[0].why_now.confidence,
AgentBriefWhyNowConfidence::High
);
assert!(selection.warnings.is_empty());
}
#[test]
fn agent_brief_selector_prefers_changed_line_intersection_over_same_file() {
let same_file = classified(
"src/pricing.rs",
12,
"pricing::below_threshold",
"amount < discount_threshold",
SeamGripClass::WeaklyGripped,
);
let touched = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::ReachableUnrevealed,
);
let touched_id = touched.seam.id().as_str().to_string();
let seams = vec![same_file, touched];
let working_set = AgentBriefResolvedWorkingSet::diff(
"change.diff",
vec![AgentBriefLine::new("src/pricing.rs", 88)],
);
let selection = select(&seams, &working_set, 3);
assert_eq!(selection.top_seams[0].seam.seam.id().as_str(), touched_id);
assert_eq!(
selection.top_seams[0].why_now.reason,
AgentBriefWhyNowReason::ChangedLineIntersectsSeam
);
assert_eq!(
selection.top_seams[1].why_now.reason,
AgentBriefWhyNowReason::SameFileSeam
);
}
#[test]
fn agent_brief_selector_caps_file_scope_deterministically() {
let seams = vec![
classified(
"src/pricing.rs",
10,
"pricing::a",
"a",
SeamGripClass::ActivationUnknown,
),
classified(
"src/pricing.rs",
11,
"pricing::b",
"b",
SeamGripClass::WeaklyGripped,
),
classified(
"src/pricing.rs",
12,
"pricing::c",
"c",
SeamGripClass::Ungripped,
),
];
let weak_id = seams[1].seam.id().as_str().to_string();
let ungripped_id = seams[2].seam.id().as_str().to_string();
let working_set =
AgentBriefResolvedWorkingSet::files(vec![PathBuf::from("src/pricing.rs")]);
let selection = select(&seams, &working_set, 2);
assert_eq!(selection.requested, 2);
assert_eq!(selection.returned, 2);
assert_eq!(selection.default, DEFAULT_AGENT_BRIEF_MAX_SEAMS);
assert_eq!(selection.hard_cap, AGENT_BRIEF_HARD_MAX_SEAMS);
assert_eq!(selected_ids(&selection), vec![weak_id, ungripped_id]);
assert_eq!(
selection.warnings,
vec!["1 additional visible seams were omitted by the brief cap"]
);
assert!(
selection
.top_seams
.iter()
.all(|entry| entry.why_now.reason == AgentBriefWhyNowReason::SameFileSeam)
);
}
#[test]
fn agent_brief_selector_routes_file_scoped_related_test_to_seam() {
let mut seam = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
related_test(&mut seam, "tests/pricing.rs", 20, RelationConfidence::High);
let seams = vec![seam];
let working_set =
AgentBriefResolvedWorkingSet::files(vec![PathBuf::from("tests/pricing.rs")]);
let selection = select(&seams, &working_set, 3);
assert_eq!(
selection.top_seams[0].why_now.reason,
AgentBriefWhyNowReason::ChangedTestForRelatedSeam
);
assert_eq!(
selection.top_seams[0].why_now.confidence,
AgentBriefWhyNowConfidence::Medium
);
assert!(
selection.top_seams[0]
.why_now
.evidence
.contains("working set includes related test")
);
}
#[test]
fn agent_brief_selector_routes_changed_owner_function_to_seam() {
let seams = vec![classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
)];
let working_set = AgentBriefResolvedWorkingSet::diff(
"change.diff",
vec![AgentBriefLine::new("src/pricing.rs", 91)],
)
.with_changed_owners(vec![AgentBriefChangedOwner::new(
"src/pricing.rs",
91,
"pricing::discounted_total",
)]);
let selection = select(&seams, &working_set, 3);
assert_eq!(
selection.top_seams[0].why_now.reason,
AgentBriefWhyNowReason::ChangedOwnerFunction
);
assert_eq!(
selection.top_seams[0].why_now.confidence,
AgentBriefWhyNowConfidence::High
);
assert!(
selection.top_seams[0]
.why_now
.evidence
.contains("inside owner pricing::discounted_total")
);
}
#[test]
fn agent_brief_selector_routes_changed_related_test_line_to_seam() {
let mut seam = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
related_test(&mut seam, "tests/pricing.rs", 20, RelationConfidence::High);
let seams = vec![seam];
let working_set = AgentBriefResolvedWorkingSet::diff(
"change.diff",
vec![AgentBriefLine::new("tests/pricing.rs", 20)],
);
let selection = select(&seams, &working_set, 3);
assert_eq!(
selection.top_seams[0].why_now.reason,
AgentBriefWhyNowReason::ChangedTestForRelatedSeam
);
assert_eq!(
selection.top_seams[0].why_now.confidence,
AgentBriefWhyNowConfidence::High
);
assert!(
selection.top_seams[0]
.why_now
.evidence
.contains("intersects related test")
);
}
#[test]
fn agent_brief_selector_routes_changed_assertion_near_related_test_to_seam() {
let mut seam = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
related_test(&mut seam, "tests/pricing.rs", 20, RelationConfidence::High);
let seams = vec![seam];
let working_set = AgentBriefResolvedWorkingSet::diff(
"change.diff",
vec![AgentBriefLine::new("tests/pricing.rs", 24)],
);
let selection = select(&seams, &working_set, 3);
assert_eq!(
selection.top_seams[0].why_now.reason,
AgentBriefWhyNowReason::ChangedAssertionNearRelatedTest
);
assert_eq!(
selection.top_seams[0].why_now.confidence,
AgentBriefWhyNowConfidence::Medium
);
assert!(
selection.top_seams[0]
.why_now
.evidence
.contains("is near related test")
);
}
#[test]
fn agent_brief_selector_ranks_assertion_near_related_test_before_file_only_related_test() {
let mut broad = classified(
"src/a.rs",
88,
"pricing::broad_file_match",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
related_test(&mut broad, "tests/shared.rs", 1, RelationConfidence::High);
let mut assertion = classified(
"src/z.rs",
88,
"pricing::assertion_match",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
related_test(
&mut assertion,
"tests/shared.rs",
20,
RelationConfidence::High,
);
let assertion_id = assertion.seam.id().as_str().to_string();
let seams = vec![broad, assertion];
let working_set = AgentBriefResolvedWorkingSet::diff(
"change.diff",
vec![AgentBriefLine::new("tests/shared.rs", 24)],
);
let selection = select(&seams, &working_set, 2);
assert_eq!(selection.top_seams[0].seam.seam.id().as_str(), assertion_id);
assert_eq!(
selection.top_seams[0].why_now.reason,
AgentBriefWhyNowReason::ChangedAssertionNearRelatedTest
);
}
#[test]
fn agent_brief_selector_uses_default_limit_when_zero_is_requested() {
let seams = vec![
classified(
"src/pricing.rs",
10,
"pricing::a",
"a",
SeamGripClass::WeaklyGripped,
),
classified(
"src/pricing.rs",
11,
"pricing::b",
"b",
SeamGripClass::Ungripped,
),
classified(
"src/pricing.rs",
12,
"pricing::c",
"c",
SeamGripClass::ReachableUnrevealed,
),
classified(
"src/pricing.rs",
13,
"pricing::d",
"d",
SeamGripClass::ActivationUnknown,
),
];
let working_set =
AgentBriefResolvedWorkingSet::files(vec![PathBuf::from("src/pricing.rs")]);
let selection = select(&seams, &working_set, 0);
assert_eq!(selection.requested, 0);
assert_eq!(selection.returned, DEFAULT_AGENT_BRIEF_MAX_SEAMS);
assert_eq!(selection.top_seams.len(), DEFAULT_AGENT_BRIEF_MAX_SEAMS);
assert_eq!(
selection.warnings,
vec!["1 additional visible seams were omitted by the brief cap"]
);
}
#[test]
fn agent_brief_grip_priority_matches_spec_order() {
let priorities = [
(SeamGripClass::WeaklyGripped, 0),
(SeamGripClass::Ungripped, 1),
(SeamGripClass::ReachableUnrevealed, 2),
(SeamGripClass::ActivationUnknown, 3),
(SeamGripClass::PropagationUnknown, 4),
(SeamGripClass::ObservationUnknown, 5),
(SeamGripClass::DiscriminationUnknown, 6),
(SeamGripClass::Opaque, 7),
(SeamGripClass::StronglyGripped, 8),
(SeamGripClass::Intentional, 9),
(SeamGripClass::Suppressed, 10),
];
for (class, expected) in priorities {
assert_eq!(grip_priority(class), expected);
}
}
#[test]
fn agent_brief_severity_priority_prefers_warning_before_info_note_and_off() {
assert_eq!(severity_priority(ConfigSeverity::Warning), 0);
assert_eq!(severity_priority(ConfigSeverity::Info), 1);
assert_eq!(severity_priority(ConfigSeverity::Note), 2);
assert_eq!(severity_priority(ConfigSeverity::Off), 3);
}
#[test]
fn agent_brief_selector_uses_related_test_confidence_before_path() {
let mut low = classified(
"src/a.rs",
88,
"pricing::low_confidence",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
related_test(&mut low, "tests/shared.rs", 20, RelationConfidence::Low);
let mut high = classified(
"src/z.rs",
88,
"pricing::high_confidence",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
related_test(&mut high, "tests/shared.rs", 20, RelationConfidence::High);
let high_id = high.seam.id().as_str().to_string();
let seams = vec![low, high];
let working_set =
AgentBriefResolvedWorkingSet::files(vec![PathBuf::from("tests/shared.rs")]);
let selection = select(&seams, &working_set, 2);
assert_eq!(selection.top_seams[0].seam.seam.id().as_str(), high_id);
}
#[test]
fn agent_brief_selector_uses_repo_fallback_when_working_set_has_no_match() {
let seam = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
let seam_id = seam.seam.id().as_str().to_string();
let seams = vec![seam];
let working_set = AgentBriefResolvedWorkingSet::files(vec![PathBuf::from("src/other.rs")]);
let selection = select(&seams, &working_set, 3);
assert_eq!(selected_ids(&selection), vec![seam_id]);
assert_eq!(
selection.top_seams[0].why_now.reason,
AgentBriefWhyNowReason::RepoActionableFallback
);
assert_eq!(
selection.top_seams[0].why_now.confidence,
AgentBriefWhyNowConfidence::Low
);
}
#[test]
fn agent_brief_selector_uses_configured_severity_before_grip_priority() -> Result<(), String> {
let weak = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
let ungripped = classified(
"src/pricing.rs",
89,
"pricing::taxed_total",
"tax > 0",
SeamGripClass::Ungripped,
);
let ungripped_id = ungripped.seam.id().as_str().to_string();
let seams = vec![weak, ungripped];
let working_set =
AgentBriefResolvedWorkingSet::files(vec![PathBuf::from("src/pricing.rs")]);
let config = tests_only_parse(
r#"
[severity.seams]
weakly_gripped = "note"
ungripped = "warning"
"#,
)?;
let selection = select_with_config(&seams, &working_set, 2, &config);
assert_eq!(selection.top_seams[0].seam.seam.id().as_str(), ungripped_id);
Ok(())
}
#[test]
fn agent_brief_selector_omits_configured_off_working_set_seam() -> Result<(), String> {
let seam = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
let fallback = classified(
"src/tax.rs",
12,
"tax::total",
"tax > 0",
SeamGripClass::Ungripped,
);
let seams = vec![seam, fallback];
let working_set =
AgentBriefResolvedWorkingSet::files(vec![PathBuf::from("src/pricing.rs")]);
let config = tests_only_parse(
r#"
[severity.seams]
weakly_gripped = "off"
"#,
)?;
let selection = select_with_config(&seams, &working_set, 3, &config);
assert!(selection.top_seams.is_empty());
assert_eq!(
selection.warnings,
vec![format!(
"seam {} at src/pricing.rs:88 is configured off for weakly_gripped seams and is not included in agent brief results",
seams[0].seam.id().as_str()
)]
);
Ok(())
}
#[test]
fn agent_brief_selector_omits_configured_off_explicit_seam_without_fallback()
-> Result<(), String> {
let hidden = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
let hidden_id = hidden.seam.id().as_str().to_string();
let fallback = classified(
"src/tax.rs",
12,
"tax::total",
"tax > 0",
SeamGripClass::Ungripped,
);
let seams = vec![hidden, fallback];
let config = tests_only_parse(
r#"
[severity.seams]
weakly_gripped = "off"
"#,
)?;
let selection = select_with_config(
&seams,
&AgentBriefResolvedWorkingSet::seam_id(&hidden_id),
3,
&config,
);
assert!(selection.top_seams.is_empty());
assert_eq!(
selection.warnings,
vec![format!(
"requested seam_id {hidden_id} is configured off for weakly_gripped seams and is not included in agent brief results"
)]
);
Ok(())
}
#[test]
fn agent_brief_selector_warns_when_explicit_seam_is_missing() {
let seam = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
let seams = vec![seam];
let selection = select(
&seams,
&AgentBriefResolvedWorkingSet::seam_id("missing-seam"),
3,
);
assert!(selection.top_seams.is_empty());
assert_eq!(
selection.warnings,
vec!["requested seam_id missing-seam was not found"]
);
}
#[test]
fn agent_brief_selector_warns_when_explicit_seam_is_hidden() {
let hidden = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::StronglyGripped,
);
let seam_id = hidden.seam.id().as_str().to_string();
let seams = vec![hidden];
let selection = select(&seams, &AgentBriefResolvedWorkingSet::seam_id(&seam_id), 3);
assert!(selection.top_seams.is_empty());
assert_eq!(
selection.warnings,
vec![format!(
"requested seam_id {seam_id} is configured off for strongly_gripped seams and is not included in agent brief results"
)]
);
}
#[test]
fn agent_brief_selector_normalizes_windows_style_paths() {
let seam = classified(
"src/pricing.rs",
88,
"pricing::discounted_total",
"amount >= discount_threshold",
SeamGripClass::WeaklyGripped,
);
let seams = vec![seam];
let working_set =
AgentBriefResolvedWorkingSet::files(vec![PathBuf::from(".\\src\\pricing.rs")]);
let selection = select(&seams, &working_set, 3);
assert_eq!(
selection.top_seams[0].why_now.reason,
AgentBriefWhyNowReason::SameFileSeam
);
}
}