use crate::{
llm::{ChatMessage, LlmRequest, ResponseSchema, strip_provider_prefix},
models::Finding,
};
const VERIFY_TEMPERATURE: f32 = 1.0;
const VERIFY_MAX_TOKENS: u32 = 64;
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
The default answer is REFUTED. Answer CONFIRMED only when the diff clearly shows
the problem the finding describes. When in doubt, REFUTE.
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;