use std::collections::HashSet;
use async_trait::async_trait;
use car_eventlog::harness_adapt::{HarnessIntervention, InterventionLayer};
use car_memgine::harness_evolution::{
mutation_fingerprint, ChangeContract, Governance, HarnessComponent, HarnessConfig,
HarnessConfigPatch, HarnessMutation, PromotionDecision,
};
use car_policy::permission::ApprovalDecision;
use super::ab_loop::{AbFixer, FixResult};
pub fn mutations_from_interventions(
interventions: &[HarnessIntervention],
current: &HarnessConfig,
) -> Vec<HarnessMutation> {
let mut out = Vec::new();
let mut seen = HashSet::new();
for iv in interventions {
if iv.layer != InterventionLayer::TrajectoryRegulation {
continue;
}
let bump = (iv.evidence_count as u32).clamp(1, 4);
let trig = iv.trigger.to_lowercase();
let (component, patch, predicted) = if trig.contains("retried") {
let target = current.max_retries.saturating_add(bump);
(
HarnessComponent::RetryConfig,
HarnessConfigPatch {
max_retries: Some(target),
..Default::default()
},
format!(
"raise max_retries {} → {} to absorb retry thrash on '{}'",
current.max_retries, target, iv.target
),
)
} else {
let target = current.planning_max_replans.saturating_add(bump);
(
HarnessComponent::PlanningConfig,
HarnessConfigPatch {
planning_max_replans: Some(target),
..Default::default()
},
format!(
"raise planning_max_replans {} → {} for recurring failure on '{}'",
current.planning_max_replans, target, iv.target
),
)
};
let mutation = HarnessMutation {
id: format!("ab:{}:{}", component_slug(component), iv.target),
contract: ChangeContract {
component,
target_failure: iv.trigger.clone(),
predicted_improvement: predicted,
invariants: vec![
"no new tool, permission, or validator surface".to_string(),
"coder's contract remains the trust boundary".to_string(),
],
falsifying_eval: "the next coder A/B round's paired pass-rate does not improve"
.to_string(),
rollback: "apply the inverse patch (restore the prior knob value)".to_string(),
},
rationale: format!(
"coder A/B attribution: {} (evidence {})",
iv.intervention, iv.evidence_count
),
patch: Some(patch),
};
if seen.insert(mutation_fingerprint(&mutation)) {
out.push(mutation);
}
}
out
}
fn component_slug(c: HarnessComponent) -> &'static str {
match c {
HarnessComponent::RetryConfig => "retry",
HarnessComponent::PlanningConfig => "planning",
HarnessComponent::ToolSchema => "tool_schema",
HarnessComponent::RetrievalPolicy => "retrieval",
HarnessComponent::ContextBudget => "context",
HarnessComponent::WorkflowTopology => "topology",
HarnessComponent::PermissionRule => "permission",
HarnessComponent::Validator => "validator",
}
}
#[async_trait]
pub trait HarnessApply: Send + Sync {
async fn approval(&self, fingerprint: &str) -> Option<ApprovalDecision>;
async fn apply(&self, mutation: &HarnessMutation, governance: Governance)
-> Result<(), String>;
}
pub struct EvolutionAbFixer<A: HarnessApply> {
pub current: HarnessConfig,
pub backend: A,
pub optimistic: bool,
}
#[async_trait]
impl<A: HarnessApply> AbFixer for EvolutionAbFixer<A> {
async fn apply(&self, interventions: &[HarnessIntervention]) -> FixResult {
let mutations = mutations_from_interventions(interventions, &self.current);
let patchless = interventions
.iter()
.filter(|i| i.layer != InterventionLayer::TrajectoryRegulation)
.count();
if mutations.is_empty() {
return FixResult {
applied: false,
note: format!(
"no auto-applicable budget knob among {} intervention(s); {} need human design (prompt/validator/permission changes are code, not knobs)",
interventions.len(),
patchless
),
};
}
let mut applied = 0usize;
let mut pending = 0usize;
let mut blocked = 0usize;
for m in &mutations {
let fp = mutation_fingerprint(m);
match self.backend.approval(&fp).await {
Some(ApprovalDecision::Rejected) => blocked += 1,
Some(ApprovalDecision::Approved) => {
if self
.backend
.apply(m, Governance::HumanApproved)
.await
.is_ok()
{
applied += 1;
} else {
blocked += 1;
}
}
None => {
if self.optimistic && !m.requires_human_approval() {
let gov = Governance::Promoted(PromotionDecision::Promote {
reason:
"non-safety budget bump; the next A/B round regression-gates it"
.into(),
});
if self.backend.apply(m, gov).await.is_ok() {
applied += 1;
} else {
pending += 1;
}
} else {
pending += 1;
}
}
}
}
FixResult {
applied: applied > 0,
note: format!(
"{applied} applied, {pending} pending approval, {blocked} blocked ({} mutation(s), {patchless} non-knob intervention(s) deferred to human design)",
mutations.len()
),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
use std::sync::Mutex;
fn iv(
layer: InterventionLayer,
target: &str,
trigger: &str,
evidence: usize,
) -> HarnessIntervention {
HarnessIntervention {
layer,
target: target.into(),
trigger: trigger.into(),
intervention: "do the thing".into(),
evidence_count: evidence,
}
}
#[test]
fn bridge_maps_only_trajectory_interventions_onto_knobs() {
let cur = HarnessConfig::default(); let ivs = vec![
iv(
InterventionLayer::TrajectoryRegulation,
"run_command",
"action 'run_command' retried 3×",
3,
),
iv(
InterventionLayer::TrajectoryRegulation,
"proposal:p1",
"replanning exhausted 2× for proposal 'p1'",
2,
),
iv(
InterventionLayer::EnvironmentContract,
"edit_file",
"rejected before execution 2×",
2,
),
iv(
InterventionLayer::ActionRealization,
"tool_x",
"no tool 'tool_x'",
4,
),
];
let muts = mutations_from_interventions(&ivs, &cur);
assert_eq!(muts.len(), 2, "only the two trajectory interventions map");
let retry = muts
.iter()
.find(|m| m.contract.component == HarnessComponent::RetryConfig)
.unwrap();
assert_eq!(retry.patch.as_ref().unwrap().max_retries, Some(6));
let plan = muts
.iter()
.find(|m| m.contract.component == HarnessComponent::PlanningConfig)
.unwrap();
assert_eq!(plan.patch.as_ref().unwrap().planning_max_replans, Some(4));
assert!(muts
.iter()
.all(|m| m.contract.falsifying_eval.contains("A/B")));
}
#[test]
fn bridge_dedups_identical_bumps() {
let cur = HarnessConfig::default();
let ivs = vec![
iv(
InterventionLayer::TrajectoryRegulation,
"t",
"action 't' failed 2×",
2,
),
iv(
InterventionLayer::TrajectoryRegulation,
"t",
"action 't' failed 2×",
2,
),
];
assert_eq!(mutations_from_interventions(&ivs, &cur).len(), 1);
}
struct FakeApply {
decisions: HashMap<String, ApprovalDecision>,
applied: Mutex<Vec<String>>,
fail_apply: bool,
}
#[async_trait]
impl HarnessApply for FakeApply {
async fn approval(&self, fp: &str) -> Option<ApprovalDecision> {
self.decisions.get(fp).cloned()
}
async fn apply(&self, m: &HarnessMutation, _g: Governance) -> Result<(), String> {
if self.fail_apply {
return Err("apply failed".into());
}
self.applied.lock().unwrap().push(mutation_fingerprint(m));
Ok(())
}
}
fn traj(target: &str, evidence: usize) -> HarnessIntervention {
iv(
InterventionLayer::TrajectoryRegulation,
target,
&format!("action '{target}' failed {evidence}×"),
evidence,
)
}
#[tokio::test]
async fn optimistic_applies_non_safety_budget_bumps() {
let fixer = EvolutionAbFixer {
current: HarnessConfig::default(),
backend: FakeApply {
decisions: HashMap::new(),
applied: Mutex::new(vec![]),
fail_apply: false,
},
optimistic: true,
};
let r = fixer.apply(&[traj("a", 2), traj("b", 1)]).await;
assert!(r.applied, "{}", r.note);
assert_eq!(fixer.backend.applied.lock().unwrap().len(), 2);
}
#[tokio::test]
async fn non_optimistic_leaves_everything_pending() {
let fixer = EvolutionAbFixer {
current: HarnessConfig::default(),
backend: FakeApply {
decisions: HashMap::new(),
applied: Mutex::new(vec![]),
fail_apply: false,
},
optimistic: false,
};
let r = fixer.apply(&[traj("a", 2)]).await;
assert!(!r.applied);
assert!(r.note.contains("1 pending"), "{}", r.note);
assert!(fixer.backend.applied.lock().unwrap().is_empty());
}
#[tokio::test]
async fn ledger_approval_and_rejection_are_honored() {
let cur = HarnessConfig::default();
let approved = &mutations_from_interventions(&[traj("a", 2)], &cur)[0];
let rejected = &mutations_from_interventions(&[traj("b", 3)], &cur)[0];
let mut decisions = HashMap::new();
decisions.insert(mutation_fingerprint(approved), ApprovalDecision::Approved);
decisions.insert(mutation_fingerprint(rejected), ApprovalDecision::Rejected);
let fixer = EvolutionAbFixer {
current: cur,
backend: FakeApply {
decisions,
applied: Mutex::new(vec![]),
fail_apply: false,
},
optimistic: false,
};
let r = fixer.apply(&[traj("a", 2), traj("b", 3)]).await;
assert!(r.applied);
assert!(
r.note.contains("1 applied") && r.note.contains("1 blocked"),
"{}",
r.note
);
assert_eq!(fixer.backend.applied.lock().unwrap().len(), 1);
}
#[tokio::test]
async fn no_knob_interventions_report_not_applied() {
let fixer = EvolutionAbFixer {
current: HarnessConfig::default(),
backend: FakeApply {
decisions: HashMap::new(),
applied: Mutex::new(vec![]),
fail_apply: false,
},
optimistic: true,
};
let r = fixer
.apply(&[iv(
InterventionLayer::EnvironmentContract,
"x",
"rejected 2×",
2,
)])
.await;
assert!(!r.applied);
assert!(r.note.contains("human design"), "{}", r.note);
}
}