use crate::{
llm::{ChatMessage, LlmRequest, ResponseSchema, strip_provider_prefix},
models::Finding,
};
const VERIFY_TEMPERATURE: f32 = 1.0;
const VERIFY_MAX_TOKENS: u32 = 128;
pub const VERIFY_SCHEMA_NAME: &str = "verification_judgment";
pub fn verifier_system_prompt() -> &'static str {
r#"You are a strict code-review fact-checker. You are given the unified diff of a
pull request and ONE finding another reviewer raised about that diff. Your only
job is to decide whether the finding is a real, defensible issue grounded in the
diff shown.
## Judgment (MANDATORY — pick exactly one)
- CONFIRMED — the finding is real: the cited problem is actually present in the
diff, the failure path is plausible, and a reasonable engineer would agree it
needs attention.
- REFUTED — the finding is NOT defensible: it is speculative, incorrect,
contradicted by the diff, or cannot be located in the diff at all.
## Hard rule (truncation / hallucination guard)
If the finding references a file or line that does NOT appear in the diff shown
below, you MUST answer REFUTED. Do not assume context you cannot see. A finding
about code that is not in the diff is, by definition, not verifiable and must be
refuted. This rule is absolute — it overrides any plausibility you might infer.
## Burden of proof
Weigh the evidence in the diff. Answer CONFIRMED when the finding is grounded in
the diff and a reasonable engineer would agree it needs attention. Answer REFUTED
when the finding is speculative, contradicted by the diff, or references code that
is not present in the diff at all. Decide on the evidence — do not default to
either verdict. If the concern is real but the diff is merely ambiguous, prefer
CONFIRMED with a qualifying reason.
Populate the structured response fields: `judgment` (CONFIRMED or REFUTED) and
`reason` (one short sentence)."#
}
pub fn verify_response_schema() -> ResponseSchema {
ResponseSchema {
name: VERIFY_SCHEMA_NAME.to_string(),
schema: serde_json::json!({
"type": "object",
"properties": {
"judgment": {
"type": "string",
"enum": ["CONFIRMED", "REFUTED"],
"description": "Binary verification judgment for the finding"
},
"reason": {
"type": "string",
"description": "One short sentence justifying the judgment"
}
},
"required": ["judgment"]
}),
}
}
pub fn build_verify_request(
verifier_model: &str,
diff: &str,
finding: &Finding,
temperature: Option<f32>,
max_tokens: Option<u32>,
) -> LlmRequest {
let line = finding
.line
.map(|l| l.to_string())
.unwrap_or_else(|| "(unspecified)".to_string());
let user_message = format!(
"## Unified diff\n\n```diff\n{diff}\n```\n\n\
## Finding to verify\n\
- file: `{file}`\n\
- line: {line}\n\
- kind: {kind}\n\
- description: {description}\n\
- proposed fix: {suggestion}\n\n\
Decide CONFIRMED or REFUTED per the rules in the system prompt. \
If `{file}` or line {line} does not appear in the diff above, answer REFUTED.",
diff = diff,
file = finding.file,
line = line,
kind = finding.kind,
description = finding.description,
suggestion = if finding.suggestion.is_empty() {
"(none)"
} else {
&finding.suggestion
},
);
LlmRequest {
model: strip_provider_prefix(verifier_model).to_string(),
system: verifier_system_prompt().to_string(),
messages: vec![ChatMessage {
role: "user".to_string(),
content: user_message,
}],
temperature: temperature.unwrap_or(VERIFY_TEMPERATURE),
max_tokens: max_tokens.unwrap_or(VERIFY_MAX_TOKENS),
response_schema: Some(verify_response_schema()),
}
}
#[cfg(test)]
#[path = "verify_prompt_tests.rs"]
mod tests;