#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Sealed(());
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Verdict {
pub model: String,
pub pass: bool,
pub reason: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Unreachable {
pub model: String,
pub error: String,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum GateOutcome {
Approved {
passes: usize,
of: usize,
seal: Sealed,
},
ContractFailed { detail: String, seal: Sealed },
PanelRejected {
passes: usize,
required: usize,
dissent: Vec<Verdict>,
seal: Sealed,
},
PanelIncomplete {
answered: usize,
required: usize,
unreachable: Vec<Unreachable>,
seal: Sealed,
},
}
impl GateOutcome {
pub fn approved(&self) -> bool {
matches!(self, GateOutcome::Approved { .. })
}
pub fn summary(&self) -> String {
match self {
GateOutcome::Approved { passes, of, .. } => {
format!("approved by {passes}/{of} reviewers, checks green")
}
GateOutcome::ContractFailed { detail, .. } => {
format!("outcome contract failed: {detail}")
}
GateOutcome::PanelRejected {
passes,
required,
dissent,
..
} => {
let why = dissent
.iter()
.map(|v| format!("{}: {}", v.model, v.reason))
.collect::<Vec<_>>()
.join("; ");
format!("{passes}/{required} required approvals — {why}")
}
GateOutcome::PanelIncomplete {
answered,
required,
unreachable,
..
} => {
let who = unreachable
.iter()
.map(|u| format!("{} ({})", u.model, u.error))
.collect::<Vec<_>>()
.join("; ");
format!(
"only {answered} reviewers answered, {required} approvals required — \
unreachable: {who}"
)
}
}
}
}
pub fn required_approvals(panel_size: usize) -> usize {
panel_size / 2 + 1
}
pub fn decide(
contract_passed: bool,
contract_detail: &str,
panel_size: usize,
verdicts: &[Verdict],
unreachable: &[Unreachable],
) -> GateOutcome {
if !contract_passed {
return GateOutcome::ContractFailed {
detail: contract_detail.to_string(),
seal: Sealed(()),
};
}
if panel_size == 0 {
return GateOutcome::ContractFailed {
detail: "no reviewers were configured; a panel of zero cannot approve".into(),
seal: Sealed(()),
};
}
let mut seen: Vec<&str> = Vec::new();
let mut answers: Vec<Verdict> = Vec::new();
for v in verdicts {
if seen.contains(&v.model.as_str()) {
continue;
}
seen.push(&v.model);
answers.push(v.clone());
}
if answers.len() > panel_size {
return GateOutcome::ContractFailed {
detail: format!(
"{} distinct verdicts for a panel of {panel_size}",
answers.len()
),
seal: Sealed(()),
};
}
let required = required_approvals(panel_size);
if answers.len() < required {
return GateOutcome::PanelIncomplete {
answered: answers.len(),
required,
unreachable: unreachable.to_vec(),
seal: Sealed(()),
};
}
let passes = answers.iter().filter(|v| v.pass).count();
if passes >= required {
return GateOutcome::Approved {
passes,
of: panel_size,
seal: Sealed(()),
};
}
GateOutcome::PanelRejected {
passes,
required,
dissent: answers.iter().filter(|v| !v.pass).cloned().collect(),
seal: Sealed(()),
}
}
pub fn review_criteria(intent_summary: &str, contract_detail: &str) -> String {
format!(
"You are reviewing an automated code change before it is opened as a pull request.\n\
\n\
Stated intent: {intent_summary}\n\
Deterministic checks: {contract_detail}\n\
\n\
The build and the project's own acceptance checks have ALREADY passed. Do not \
re-litigate them. Judge only what they cannot:\n\
- Does the change do what the intent says, or something adjacent to it?\n\
- Does it introduce a defect the checks would not catch?\n\
- Is it scoped to the intent, or does it change unrelated behaviour?\n\
\n\
Answer PASS or FAIL and one sentence of reason. Default to FAIL if you are \
uncertain: a rejected change costs one retry, an approved bad one costs a human's \
trust in every later change."
)
}
#[cfg(test)]
mod tests {
use super::*;
fn v(model: &str, pass: bool) -> Verdict {
Verdict {
model: model.into(),
pass,
reason: if pass { "looks right" } else { "wrong scope" }.into(),
}
}
#[test]
fn a_red_contract_is_never_put_to_the_panel() {
let out = decide(
false,
"cargo test failed",
3,
&[v("a", true), v("b", true), v("c", true)],
&[],
);
assert!(matches!(out, GateOutcome::ContractFailed { .. }));
assert!(!out.approved());
}
#[test]
fn a_strict_majority_approves() {
let out = decide(
true,
"green",
3,
&[v("a", true), v("b", true), v("c", false)],
&[],
);
assert!(matches!(
out,
GateOutcome::Approved {
passes: 2,
of: 3,
..
}
));
}
#[test]
fn a_minority_does_not_approve() {
let out = decide(
true,
"green",
3,
&[v("a", true), v("b", false), v("c", false)],
&[],
);
match out {
GateOutcome::PanelRejected {
passes,
required,
dissent,
..
} => {
assert_eq!((passes, required), (1, 2));
assert_eq!(dissent.len(), 2, "dissent is recorded, not just counted");
}
other => panic!("expected rejection, got {other:?}"),
}
}
#[test]
fn an_unreachable_vendor_cannot_halve_the_panel() {
let out = decide(
true,
"green",
3,
&[v("a", true)],
&[Unreachable {
model: "b".into(),
error: "timeout".into(),
}],
);
match out {
GateOutcome::PanelIncomplete {
answered, required, ..
} => assert_eq!((answered, required), (1, 2)),
other => panic!("expected incomplete, got {other:?}"),
}
}
#[test]
fn exactly_enough_answers_can_still_approve() {
let out = decide(true, "green", 3, &[v("a", true), v("b", true)], &[]);
assert!(matches!(
out,
GateOutcome::Approved {
passes: 2,
of: 3,
..
}
));
}
#[test]
fn exactly_enough_answers_can_also_reject() {
let out = decide(true, "green", 3, &[v("a", true), v("b", false)], &[]);
assert!(matches!(out, GateOutcome::PanelRejected { .. }));
}
#[test]
fn a_single_reviewer_cannot_approve_alone_on_a_panel_of_three() {
assert_eq!(required_approvals(3), 2);
assert_eq!(required_approvals(5), 3);
assert_eq!(required_approvals(1), 1);
}
#[test]
fn incomplete_and_rejected_are_different_outcomes() {
let incomplete = decide(true, "green", 3, &[], &[]);
let rejected = decide(true, "green", 3, &[v("a", false), v("b", false)], &[]);
assert!(matches!(incomplete, GateOutcome::PanelIncomplete { .. }));
assert!(matches!(rejected, GateOutcome::PanelRejected { .. }));
}
#[test]
fn the_summary_names_the_dissenters_and_the_unreachable() {
let rejected = decide(true, "green", 3, &[v("a", true), v("b", false)], &[]);
assert!(rejected.summary().contains('b'), "{}", rejected.summary());
let incomplete = decide(
true,
"green",
3,
&[],
&[Unreachable {
model: "gpt".into(),
error: "429".into(),
}],
);
assert!(incomplete.summary().contains("gpt"));
assert!(incomplete.summary().contains("429"));
}
#[test]
fn the_criteria_do_not_carry_the_issue_body() {
let c = review_criteria("fix the off-by-one in the parser", "all checks green");
assert!(c.contains("off-by-one"));
assert!(
c.contains("Default to FAIL"),
"uncertainty must not read as approval"
);
}
#[test]
fn every_reviewer_is_asked_the_same_question() {
let a = review_criteria("intent", "green");
let b = review_criteria("intent", "green");
assert_eq!(
a, b,
"a panel prompted differently measures prompt variance"
);
}
}