use std::fmt::Write as _;
use crate::compile_claim::{ClaimSite, Features, TargetOs};
use crate::review_corpus::{CLASSES, DefectClass};
use crate::review_score::CandidateFinding;
pub const SINGLE_CALL_BUDGET_TOKENS: usize = 30_000;
#[must_use]
pub fn estimate_tokens(text: &str) -> usize {
text.len() / 4
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileUnderReview {
pub reviewed_sha: String,
pub path: String,
pub diff: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContextItem {
pub label: String,
pub provenance: String,
pub body: String,
}
pub const CONTEXT_CAP_TOKENS: usize = 4_000;
pub const CONTEXT_RELATIVE_TO_DIFF: usize = 2;
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct GraphContext {
pub items: Vec<ContextItem>,
pub dropped_items: usize,
}
impl GraphContext {
#[must_use]
pub fn none() -> Self {
Self::default()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
#[must_use]
pub fn tokens(&self) -> usize {
self.items.iter().map(ContextItem::tokens).sum()
}
#[must_use]
pub fn fit(items: Vec<ContextItem>, diff_tokens: usize) -> Self {
let cap = CONTEXT_CAP_TOKENS.min(CONTEXT_RELATIVE_TO_DIFF.saturating_mul(diff_tokens));
let mut kept: Vec<ContextItem> = Vec::new();
let mut spent = 0usize;
let mut dropped = 0usize;
for item in items {
let cost = item.tokens();
if spent + cost > cap {
dropped += 1;
continue;
}
spent += cost;
kept.push(item);
}
Self {
items: kept,
dropped_items: dropped,
}
}
}
impl ContextItem {
#[must_use]
pub fn tokens(&self) -> usize {
estimate_tokens(&self.label)
+ estimate_tokens(&self.provenance)
+ estimate_tokens(&self.body)
+ 4
}
}
#[must_use]
pub fn section_body(markdown: &str, title: &str) -> Option<String> {
let mut out: Option<String> = None;
for line in markdown.lines() {
if let Some(heading) = line.strip_prefix("## ") {
if out.is_some() {
break;
}
if heading.trim() == title.trim() {
out = Some(String::new());
}
continue;
}
if let Some(body) = out.as_mut() {
body.push_str(line);
body.push('\n');
}
}
out.map(|b| b.trim().to_owned())
}
const DOC_PROBE_CHARS: usize = 60;
fn doc_signature(text: &str, strip_annotation_column: bool) -> String {
let mut out = String::with_capacity(text.len());
for line in text.lines() {
let body = if strip_annotation_column {
line.split_once('|').map_or(line, |(_, rest)| rest)
} else {
line
};
let body = body.trim_start();
let body = body
.strip_prefix("///")
.or_else(|| body.strip_prefix("//!"))
.or_else(|| body.strip_prefix("//"))
.unwrap_or(body);
out.extend(body.chars().filter(|c| !c.is_whitespace()));
}
out
}
#[must_use]
pub fn doc_already_shown(doc: &str, annotated_diff: &str) -> bool {
let probe: String = doc_signature(doc, false)
.chars()
.take(DOC_PROBE_CHARS)
.collect();
if probe.chars().count() < DOC_PROBE_CHARS {
return true;
}
doc_signature(annotated_diff, true).contains(&probe)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Prompt {
pub text: String,
pub tokens: usize,
pub dropped_tokens: usize,
}
const FINDING_PREFIX: &str = "FINDING";
const NO_FINDINGS: &str = "NO FINDINGS";
#[must_use]
pub fn build_prompt(file: &FileUnderReview, context: &GraphContext, budget: usize) -> Prompt {
let mut head = String::new();
head.push_str(
"You are reviewing ONE FILE of a change to a Rust codebase.\n\n\
The defects that matter here are **contract-accuracy** defects: code that \
runs correctly but does not mean what it says. They compile, they pass \
tests, and CI is green on them by definition — so they are found only by \
reading the words against the behaviour. Do not look for crashes or \
compile errors; look for places where a promise and its implementation \
have come apart.\n\n\
Work through the change against each of these, in order:\n\n",
);
for class in CLASSES {
let _ = writeln!(head, " {} — {}", class.as_str(), class_gloss(class));
}
head.push_str(
"\nFor each one, ask specifically:\n\
- Does a doc comment, `///` line, README sentence or ADR in this diff \
state something the code beside it does not do? Compare the two texts \
word by word — a doc that describes the old behaviour after the code \
moved on is the single most common defect in this codebase.\n\
- Does an error message name the rule it actually enforces, or a \
different one?\n\
- Does a test assert the behaviour its name claims, or would it pass with \
the feature removed?\n\
- Does a check permit the state it exists to forbid (off-by-one, wrong \
comparison, missing case)?\n\
- Is a key, hash or id built from something lossy, so two different \
inputs collide?\n\n\
Output format. One finding per line, nothing else on the line:\n\
\x20 FINDING | line=<n> | class=<class> | compile=<yes|no> | <one sentence>\n\n\
For example:\n\
\x20 FINDING | line=214 | class=contract-drift | compile=no | the doc says \
the cache is unbounded but `insert` evicts at 256 entries\n\
\x20 FINDING | line=87 | class=permissive-constraint | compile=no | uses \
`<=` so a zero-length span passes the guard that exists to reject it\n\n\
Rules:\n\
- Cite the NEW-SIDE line number from the left column. Every line is \
numbered for you; never compute one from the hunk header.\n\
- **Both halves must be visible below.** Report a conflict only when the \
promise AND the behaviour that breaks it are both in the lines shown. If \
you can see a doc comment but not the code it describes, or a call but \
not the signature it calls, you cannot tell whether they disagree — say \
nothing. Do not infer what code you have not been shown does.\n\
- Quote the specific words that conflict, so a reader can check you \
without opening the file.\n\
- Do not restate one point as several findings. Each finding must be a \
separate defect a separate commit would fix.\n\
- `compile=yes` ONLY if you are claiming the code will not build. \
Everything else is `compile=no`.\n",
);
let _ = writeln!(
head,
" - Reply {NO_FINDINGS} only if you have worked through every class \
above and found nothing. A file whose change is routine is a normal \
outcome, and reporting nothing is better than reporting a guess."
);
let mut context_block = String::new();
if !context.is_empty() {
context_block.push_str(
"\nContext from the repository's graph. This is not part of the \
change; it is what the graph knows about the code under review, and \
each item says which layer it came from.\n\n",
);
for item in &context.items {
let _ = writeln!(
context_block,
"--- {} [{}]\n{}",
item.label,
item.provenance,
item.body.trim_end()
);
}
}
let annotated = annotate_diff(&file.diff);
let tail_header = format!("\nFile under review: {}\n\n", file.path);
let fixed =
estimate_tokens(&head) + estimate_tokens(&context_block) + estimate_tokens(&tail_header);
let room = budget.saturating_sub(fixed);
let (body, dropped_tokens) = truncate_to_tokens(&annotated, room);
let text = format!("{head}{context_block}{tail_header}{body}");
Prompt {
tokens: estimate_tokens(&text),
text,
dropped_tokens,
}
}
fn class_gloss(class: DefectClass) -> &'static str {
match class {
DefectClass::CleanupGap => "a guard stops a cleanup path doing its job",
DefectClass::ContractDrift => {
"a doc comment, README or ADR states something the code does not do"
}
DefectClass::ErrorTextDrift => "an error message does not state the rule it enforces",
DefectClass::FalseCompileClaim => "the code will not compile (see the compile= rule)",
DefectClass::LintConvention => "a lint suppression carries no justification",
DefectClass::LossyIdentity => {
"a key built from a lossy conversion, so distinct inputs collide"
}
DefectClass::MissingEvent => "an early return skips a documented side effect",
DefectClass::OrderingBug => "an aggregate is computed after the mutation it must precede",
DefectClass::PerfContract => "the implementation defeats a field's stated design goal",
DefectClass::PermissiveConstraint => "a check permits the state it exists to forbid",
DefectClass::ProseClarity => "wording that misleads a reader",
DefectClass::SilentTruncation => "a read or copy drops a remainder without erroring",
DefectClass::UxDiagnostic => "a message tells the user to do the wrong thing",
DefectClass::VacuousTest => "a test passes whether or not the behaviour it names works",
}
}
#[must_use]
pub fn annotate_diff(diff: &str) -> String {
let mut out = String::with_capacity(diff.len() + diff.len() / 8);
let mut new_line: Option<u32> = None;
for raw in diff.lines() {
if raw.starts_with("@@") {
new_line = parse_hunk_new_start(raw);
out.push_str(raw);
out.push('\n');
continue;
}
let Some(n) = new_line else {
out.push_str(raw);
out.push('\n');
continue;
};
match raw.as_bytes().first() {
Some(b'-') => {
let _ = writeln!(out, " - |{}", &raw[1..]);
}
Some(b'+') => {
let _ = writeln!(out, "{n:>6} +|{}", &raw[1..]);
new_line = Some(n + 1);
}
Some(b'\\') => {
let _ = writeln!(out, " |{raw}");
}
_ => {
let body = raw.strip_prefix(' ').unwrap_or(raw);
let _ = writeln!(out, "{n:>6} |{body}");
new_line = Some(n + 1);
}
}
}
out
}
fn parse_hunk_new_start(header: &str) -> Option<u32> {
let plus = header.split('+').nth(1)?;
let digits: String = plus.chars().take_while(char::is_ascii_digit).collect();
digits.parse().ok()
}
fn truncate_to_tokens(text: &str, budget: usize) -> (String, usize) {
const MARKER: &str =
"\n[... truncated to fit the context budget: this is PART of the file ...]\n";
if estimate_tokens(text) <= budget {
return (text.to_owned(), 0);
}
let room = budget.saturating_sub(estimate_tokens(MARKER)) * 4;
let mut kept = 0usize;
for line in text.split_inclusive('\n') {
if kept + line.len() > room {
break;
}
kept += line.len();
}
let dropped = estimate_tokens(&text[kept..]);
(format!("{}{MARKER}", &text[..kept]), dropped)
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct Parsed {
pub findings: Vec<CandidateFinding>,
pub unparsed: Vec<String>,
pub declared_clean: bool,
pub reasoning_truncated: bool,
}
#[must_use]
pub fn parse_findings(reviewed_sha: &str, path: &str, reply: &str) -> Parsed {
let mut out = Parsed {
reasoning_truncated: reply.contains("<think>"),
..Parsed::default()
};
for raw in reply.lines() {
let line = raw.trim().trim_start_matches(['-', '*', '>', '#', ' ']);
let line = line.trim_start_matches('`').trim_end();
let line = line.strip_suffix("**").unwrap_or(line).trim();
if line.eq_ignore_ascii_case(NO_FINDINGS) {
out.declared_clean = true;
continue;
}
if !line
.get(..FINDING_PREFIX.len())
.is_some_and(|p| p.eq_ignore_ascii_case(FINDING_PREFIX))
{
continue;
}
match parse_one(reviewed_sha, path, line) {
Some(finding) => out.findings.push(finding),
None => out.unparsed.push(line.to_owned()),
}
}
out
}
fn parse_one(reviewed_sha: &str, path: &str, line: &str) -> Option<CandidateFinding> {
let mut number: Option<u32> = None;
let mut class = None;
let mut claims_compile_failure = false;
let mut description = String::new();
for field in line.split('|').skip(1) {
let field = field.trim().trim_end_matches('`').trim();
let structural = field.replace("**", "");
let key_value = structural
.split_once('=')
.map(|(k, v)| (k.trim().to_ascii_lowercase(), v.trim()));
match key_value.as_ref().map(|(k, v)| (k.as_str(), *v)) {
Some(("line", value)) => number = value.parse().ok().filter(|n| *n > 0),
Some(("class", value)) => class = DefectClass::from_token(&value.to_ascii_lowercase()),
Some(("compile", value)) => {
claims_compile_failure = matches!(
value.to_ascii_lowercase().as_str(),
"yes" | "true" | "y" | "1"
);
}
_ => {
if !description.is_empty() {
description.push_str(" | ");
}
description.push_str(field);
}
}
}
Some(CandidateFinding {
reviewed_sha: reviewed_sha.to_owned(),
path: path.to_owned(),
line: number?,
description: if description.trim().is_empty() {
"(no description given)".to_owned()
} else {
description.trim().to_owned()
},
claims_compile_failure,
defect_class: class,
})
}
#[must_use]
pub fn claim_site(
reviewed_sha: &str,
path: &str,
line: u32,
source: &str,
parent_source: Option<&str>,
) -> ClaimSite {
ClaimSite {
platform: required_platform(source),
features: parent_source.and_then(|p| module_feature_gate(path, p)),
is_test_code: is_test_code(path, line, source),
toolchain: None,
..ClaimSite::unknown(reviewed_sha, path)
}
}
fn required_platform(source: &str) -> Option<TargetOs> {
for (needle, os) in [
("target_os = \"macos\"", TargetOs::MacOs),
("target_os = \"windows\"", TargetOs::Windows),
] {
if source.contains(needle) {
return Some(os);
}
}
None
}
fn is_test_code(path: &str, line: u32, source: &str) -> bool {
if ["tests/", "benches/", "examples/"]
.iter()
.any(|d| path.starts_with(d) || path.contains(&format!("/{d}")))
{
return true;
}
source
.lines()
.position(|l| l.trim_start().starts_with("#[cfg(test)]"))
.is_some_and(|idx| line as usize > idx + 1)
}
fn declaring_name(path: &str) -> Option<&str> {
let stem = path.rsplit('/').next()?.strip_suffix(".rs")?;
if stem == "mod" {
path.rsplit('/').nth(1)
} else {
Some(stem)
}
}
fn module_feature_gate(path: &str, parent_source: &str) -> Option<Features> {
let stem = declaring_name(path)?;
let lines: Vec<&str> = parent_source.lines().collect();
let decl = lines.iter().position(|l| {
let t = l.trim_start().trim_start_matches("pub ").trim_start();
t.starts_with(&format!("mod {stem};")) || t.starts_with(&format!("mod {stem} "))
})?;
for above in lines[..decl].iter().rev() {
let t = above.trim();
if t.is_empty() || t.starts_with("//") {
continue;
}
if !t.starts_with("#[") {
break;
}
if t.contains("cfg(feature") || t.contains("cfg(all(feature") {
return Some(Features::All);
}
}
None
}
#[cfg(test)]
mod tests {
use super::{
FileUnderReview, GraphContext, NO_FINDINGS, Prompt, SINGLE_CALL_BUDGET_TOKENS,
annotate_diff, build_prompt, claim_site, class_gloss, estimate_tokens, parse_findings,
};
use crate::compile_claim::{CheckRun, Conclusion, Features, TargetOs, Targets, suppression};
use crate::review_corpus::{CLASSES, DefectClass};
const SHA: &str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
fn file(diff: &str) -> FileUnderReview {
FileUnderReview {
reviewed_sha: SHA.to_owned(),
path: "crates/rto-graph/src/lib.rs".to_owned(),
diff: diff.to_owned(),
}
}
#[test]
fn the_annotated_diff_numbers_the_new_side_exactly() {
let diff = "@@ -10,3 +20,4 @@ fn thing()\n unchanged\n-gone\n+added\n+also\n context\n";
let out = annotate_diff(diff);
let numbered: Vec<(u32, String)> = out
.lines()
.filter_map(|l| {
let (num, body) = l.split_once('|')?;
let n: u32 = num
.trim()
.trim_end_matches(['+', '-'])
.trim()
.parse()
.ok()?;
Some((n, body.to_owned()))
})
.collect();
assert_eq!(
numbered,
vec![
(20, "unchanged".to_owned()),
(21, "added".to_owned()),
(22, "also".to_owned()),
(23, "context".to_owned()),
],
"new-side numbering starts at the hunk's + start and skips removals"
);
assert!(out.contains(" - |gone"), "{out}");
}
#[test]
fn numbering_restarts_at_each_hunk() {
let diff = "@@ -1,2 +1,2 @@\n a\n b\n@@ -50,2 +90,2 @@\n c\n d\n";
let out = annotate_diff(diff);
assert!(out.contains(" 1 |a"), "{out}");
assert!(out.contains(" 90 |c"), "{out}");
assert!(out.contains(" 91 |d"), "{out}");
}
#[test]
fn class_gloss_covers_every_class() {
for class in CLASSES {
let gloss = class_gloss(class);
assert!(!gloss.is_empty(), "{class} has no gloss");
}
let prompt = build_prompt(&file("@@ -1 +1 @@\n+x\n"), &GraphContext::none(), 30_000);
for class in CLASSES {
assert!(
prompt.text.contains(class.as_str()),
"{class} is not named in the prompt"
);
}
}
fn item(label: &str, bytes: usize) -> super::ContextItem {
super::ContextItem {
label: label.to_owned(),
provenance: "authored".to_owned(),
body: "x".repeat(bytes),
}
}
#[test]
fn context_is_capped_relative_to_the_diff_it_accompanies() {
let fitted = GraphContext::fit(vec![item("a", 600), item("b", 600)], 100);
assert_eq!(
fitted.items.len(),
1,
"two 150-token items fit a 200-token cap"
);
assert_eq!(fitted.dropped_items, 1);
assert!(
fitted.tokens() <= 200,
"cap breached: {} tokens",
fitted.tokens()
);
}
#[test]
fn the_absolute_cap_binds_on_a_large_diff() {
let fitted = GraphContext::fit(
(0..20).map(|i| item(&format!("adr-{i}"), 2_000)).collect(),
20_000,
);
assert!(
fitted.tokens() <= super::CONTEXT_CAP_TOKENS,
"the absolute cap did not bind: {} tokens",
fitted.tokens()
);
assert!(
fitted.dropped_items > 0,
"nothing was dropped, so nothing was capped"
);
}
#[test]
fn fitting_never_truncates_an_item_it_keeps() {
let original = item("adr", 400);
let fitted = GraphContext::fit(vec![original.clone(), item("big", 100_000)], 1_000);
assert_eq!(fitted.items, vec![original], "a kept item was rewritten");
assert_eq!(fitted.dropped_items, 1);
}
#[test]
fn an_oversized_item_does_not_evict_the_smaller_ones_after_it() {
let fitted = GraphContext::fit(vec![item("huge", 100_000), item("small", 40)], 1_000);
assert_eq!(fitted.dropped_items, 1);
assert_eq!(
fitted.items.len(),
1,
"the small item behind an oversized one was lost"
);
assert_eq!(fitted.items[0].label, "small");
}
#[test]
fn an_empty_diff_admits_no_context() {
let fitted = GraphContext::fit(vec![item("adr", 40)], 0);
assert!(fitted.is_empty());
assert_eq!(fitted.dropped_items, 1, "the drop must still be counted");
}
#[test]
fn an_item_is_charged_for_its_heading_as_well_as_its_body() {
let bare = super::ContextItem {
label: String::new(),
provenance: String::new(),
body: "x".repeat(40),
};
let labelled = super::ContextItem {
label: "ADR-0019 §3 governs `resolve`".to_owned(),
provenance: "authored".to_owned(),
body: "x".repeat(40),
};
assert!(
labelled.tokens() > bare.tokens(),
"the heading was not charged: {} vs {}",
labelled.tokens(),
bare.tokens()
);
}
#[test]
fn a_section_body_stops_at_the_next_sibling_heading() {
let md = "# ADR-0005\n\n## Context\nwhy\n\n## Decision\nthe rule\n\n### Detail\nmore\n\n## Consequences\nafter\n";
assert_eq!(
super::section_body(md, "Decision").as_deref(),
Some("the rule\n\n### Detail\nmore"),
"a `###` subheading must not end the section"
);
assert_eq!(super::section_body(md, "Context").as_deref(), Some("why"));
assert_eq!(super::section_body(md, "Absent"), None);
}
#[test]
fn a_heading_with_punctuation_resolves_without_a_slug_rule() {
let md = "## Options considered + consequences\nbody\n\n## Next\nx\n";
assert_eq!(
super::section_body(md, "Options considered + consequences").as_deref(),
Some("body")
);
}
#[test]
fn a_doc_already_in_the_diff_is_not_re_quoted() {
let doc = "Returns the cache entry for `key`, evicting the least recently used entry when the cache is full.";
let shown = format!(" 12 |/// {doc}\n 13 |pub fn get(&self) {{}}\n");
assert!(
super::doc_already_shown(doc, &shown),
"the doc is in the diff and was not recognised"
);
assert!(
!super::doc_already_shown(doc, " 12 |pub fn unrelated() {}\n"),
"a doc absent from the diff was treated as shown"
);
}
#[test]
fn the_visibility_test_ignores_the_line_number_column() {
let doc = "The slot lock is held only long enough to hand out an `Arc`, never across initialisation.";
let shown = " 16 +|/// The slot lock is held only long enough to hand\n 17 +|/// out an `Arc`, never across initialisation.\n";
assert!(
super::doc_already_shown(doc, shown),
"wrapping and numbering defeated the visibility test"
);
}
#[test]
fn a_doc_too_short_to_state_a_contract_is_never_carried() {
assert!(super::doc_already_shown("The key.", "unrelated diff text"));
}
#[test]
fn an_empty_context_renders_no_context_section() {
let bare = build_prompt(&file("@@ -1 +1 @@\n+x\n"), &GraphContext::none(), 30_000);
assert!(!bare.text.contains("Context from"), "{}", bare.text);
assert!(
!bare.text.contains("[authored]") && !bare.text.contains("[derived]"),
"no provenance labels without context: {}",
bare.text
);
let with = build_prompt(
&file("@@ -1 +1 @@\n+x\n"),
&GraphContext {
items: vec![super::ContextItem {
label: "ADR-0019 §3".to_owned(),
provenance: "authored".to_owned(),
body: "the user layer alone never suffices".to_owned(),
}],
dropped_items: 0,
},
30_000,
);
assert!(with.text.contains("Context from"), "{}", with.text);
assert!(with.text.contains("ADR-0019 §3"), "{}", with.text);
assert!(
with.text.contains("[authored]"),
"provenance travels with the item: {}",
with.text
);
}
#[test]
fn a_prompt_respects_its_budget_and_reports_what_it_dropped() {
let big = format!(
"@@ -1,1 +1,{0} @@\n{}",
"+a line of code here\n".repeat(20_000)
);
let f = file(&big);
let Prompt {
text,
tokens,
dropped_tokens,
} = build_prompt(&f, &GraphContext::none(), 8_000);
assert!(tokens <= 8_000, "over budget: {tokens}");
assert!(dropped_tokens > 0, "a 20k-line diff cannot have fit");
assert!(
text.contains("truncated to fit"),
"the model is told it is seeing part of a file"
);
let small = build_prompt(&file("@@ -1 +1 @@\n+x\n"), &GraphContext::none(), 30_000);
assert_eq!(small.dropped_tokens, 0);
assert!(!small.text.contains("truncated"));
}
#[test]
fn the_prompt_scaffolding_leaves_the_measured_headroom_intact() {
let line = format!("+{}\n", "a".repeat(43));
let worst = line.repeat(14_034 * 4 / 44);
let f = file(&format!("@@ -1,1 +1,1 @@\n{worst}"));
let p = build_prompt(&f, &GraphContext::none(), SINGLE_CALL_BUDGET_TOKENS);
assert_eq!(
p.dropped_tokens, 0,
"the corpus's largest source file must not need truncating"
);
let headroom = SINGLE_CALL_BUDGET_TOKENS - p.tokens;
assert!(
headroom > 10_000,
"only {headroom} tokens left for graph context on the worst source \
file; the arm needs room to be testable at all"
);
let median = file(&format!("@@ -1,1 +1,1 @@\n{}", line.repeat(1_476 * 4 / 44)));
let p = build_prompt(&median, &GraphContext::none(), SINGLE_CALL_BUDGET_TOKENS);
assert!(
SINGLE_CALL_BUDGET_TOKENS - p.tokens > 25_000,
"the median file should leave ~28k free, left {}",
SINGLE_CALL_BUDGET_TOKENS - p.tokens
);
}
#[test]
fn a_well_formed_finding_parses() {
let reply = "FINDING | line=42 | class=contract-drift | compile=no | the doc says X";
let parsed = parse_findings(SHA, "src/a.rs", reply);
assert_eq!(parsed.findings.len(), 1);
let f = &parsed.findings[0];
assert_eq!(f.line, 42);
assert_eq!(f.defect_class, Some(DefectClass::ContractDrift));
assert!(!f.claims_compile_failure);
assert_eq!(f.description, "the doc says X");
assert!(parsed.unparsed.is_empty());
}
#[test]
fn presentation_is_tolerated_but_content_is_not() {
let reply = "\
- **FINDING** | line=7 | class=vacuous-test | compile=no | asserts nothing
> FINDING | line=9 | class=ordering-bug | compile=YES | will not build
FINDING | class=prose-clarity | compile=no | no line at all
FINDING | line=0 | class=prose-clarity | compile=no | line zero is not a line
here is some prose the model added";
let parsed = parse_findings(SHA, "src/a.rs", reply);
assert_eq!(parsed.findings.len(), 2, "{:?}", parsed.findings);
assert_eq!(parsed.findings[0].line, 7);
assert!(
parsed.findings[1].claims_compile_failure,
"compile= is case-insensitive"
);
assert_eq!(parsed.unparsed.len(), 2, "{:?}", parsed.unparsed);
assert!(!parsed.unparsed.iter().any(|u| u.contains("here is some")));
}
#[test]
fn bold_is_stripped_from_fields_and_left_in_descriptions() {
let reply = "\
FINDING | **line**=42 | class=**contract-drift** | **compile**=YES | the **remote** path is not gated
**NO FINDINGS**";
let parsed = parse_findings(SHA, "src/a.rs", reply);
assert_eq!(parsed.findings.len(), 1, "{:?}", parsed.unparsed);
let f = &parsed.findings[0];
assert_eq!(f.line, 42, "a bolded key still names the line field");
assert_eq!(
f.defect_class,
DefectClass::from_token("contract-drift"),
"a bolded value still resolves to its class"
);
assert!(
f.claims_compile_failure,
"a bolded key still names `compile`"
);
assert_eq!(
f.description, "the **remote** path is not gated",
"the model's emphasis is the model's; the parser does not edit prose \
it is about to report"
);
assert!(parsed.declared_clean);
}
#[test]
fn a_clean_declaration_is_distinguishable_from_silence() {
let clean = parse_findings(SHA, "src/a.rs", NO_FINDINGS);
assert!(clean.declared_clean);
assert!(clean.findings.is_empty() && clean.unparsed.is_empty());
assert!(!clean.reasoning_truncated);
let waffle = parse_findings(SHA, "src/a.rs", "I reviewed the file and it looks fine.");
assert!(
!waffle.declared_clean,
"prose is not the declaration the format requires"
);
}
#[test]
fn a_reply_cut_off_inside_a_reasoning_block_is_not_a_clean_file() {
let cut = parse_findings(
SHA,
"src/a.rs",
"<think>\nLet me check the doc against the code. Line 12 says the cache is\n\
unbounded, and the insert path",
);
assert!(
cut.reasoning_truncated,
"an unterminated block is truncation"
);
assert!(!cut.declared_clean, "and it is emphatically not clean");
assert!(cut.findings.is_empty());
let finished = parse_findings(SHA, "src/a.rs", NO_FINDINGS);
assert!(!finished.reasoning_truncated);
}
#[test]
fn an_unknown_class_does_not_cost_the_finding() {
let parsed = parse_findings(
SHA,
"src/a.rs",
"FINDING | line=3 | class=off-by-one | compile=no | oops",
);
assert_eq!(parsed.findings.len(), 1);
assert_eq!(parsed.findings[0].defect_class, None);
}
#[test]
fn a_description_may_contain_the_separator() {
let parsed = parse_findings(
SHA,
"src/a.rs",
"FINDING | line=3 | class=prose-clarity | compile=no | says a | b but means a",
);
assert_eq!(parsed.findings[0].description, "says a | b but means a");
}
#[test]
fn a_macos_gated_file_yields_an_unrefutable_site() {
let source = "#[cfg(target_os = \"macos\")]\nfn teardown() {}\n";
let site = claim_site(SHA, "crates/rto-llama/src/backend.rs", 2, source, None);
assert_eq!(site.platform, Some(TargetOs::MacOs));
assert!(!suppression(&site, &ci()).is_refuted());
let plain = claim_site(
SHA,
"crates/rto-llama/src/backend.rs",
2,
"fn t() {}\n",
None,
);
assert_eq!(plain.platform, None);
assert!(suppression(&plain, &ci()).is_refuted());
}
#[test]
fn test_code_is_decided_per_line_not_per_file() {
let source = "fn real() {}\n#[cfg(test)]\nmod tests {\n fn t() {}\n}\n";
let above = claim_site(SHA, "crates/rto-graph/src/lib.rs", 1, source, None);
assert!(!above.is_test_code);
let below = claim_site(SHA, "crates/rto-graph/src/lib.rs", 4, source, None);
assert!(below.is_test_code);
let integration = claim_site(SHA, "crates/rto-graph/tests/review_corpus.rs", 1, "", None);
assert!(integration.is_test_code);
}
#[test]
fn a_feature_gated_module_needs_an_all_features_job() {
let parent = "pub mod subprocess;\n#[cfg(feature = \"exec-boxlite\")]\npub mod boxlite;\n";
let site = claim_site(
SHA,
"crates/rto-exec/src/boxlite.rs",
10,
"fn run() {}\n",
Some(parent),
);
assert_eq!(site.features, Some(Features::All));
let sibling = claim_site(
SHA,
"crates/rto-exec/src/subprocess.rs",
10,
"fn run() {}\n",
Some(parent),
);
assert_eq!(sibling.features, None);
}
#[test]
fn a_mod_rs_is_gated_by_the_declaration_of_its_directory() {
let parent = "#[cfg(feature = \"serve\")]\npub mod thing;\n";
let site = claim_site(
SHA,
"crates/x/src/thing/mod.rs",
10,
"fn run() {}\n",
Some(parent),
);
assert_eq!(
site.features,
Some(Features::All),
"`thing/mod.rs` is declared by `mod thing;`, so the gate on it applies"
);
let ungated = claim_site(
SHA,
"crates/x/src/other/mod.rs",
10,
"fn run() {}\n",
Some(parent),
);
assert_eq!(
ungated.features, None,
"the gate governs `thing`, not every `mod.rs`"
);
assert_eq!(
claim_site(SHA, "mod.rs", 1, "", Some(parent)).features,
None
);
}
#[test]
fn a_gate_on_another_item_is_not_borrowed() {
let parent = "#[cfg(feature = \"serve\")]\npub mod served;\n\npub mod plain;\n";
let site = claim_site(SHA, "crates/x/src/plain.rs", 1, "", Some(parent));
assert_eq!(
site.features, None,
"the gate governs `served`, not `plain`"
);
}
#[test]
fn token_estimation_is_the_documented_basis() {
assert_eq!(estimate_tokens(&"a".repeat(400)), 100);
assert_eq!(estimate_tokens(""), 0);
}
fn ci() -> Vec<CheckRun> {
vec![
CheckRun {
job: "msrv".to_owned(),
sha: SHA.to_owned(),
conclusion: Conclusion::Success,
toolchain: "1.94".to_owned(),
platform: TargetOs::Linux,
features: Features::All,
targets: Targets::LibsAndBins,
},
CheckRun {
job: "checks".to_owned(),
sha: SHA.to_owned(),
conclusion: Conclusion::Success,
toolchain: "stable".to_owned(),
platform: TargetOs::Linux,
features: Features::All,
targets: Targets::AllTargets,
},
]
}
}