mod common;
use common::fixture_with_quota_on_agents;
use magi::graph::Runner;
common::e2e! {
async fn a_solo_seat_falls_through_to_the_next_agent_and_recovers() {
let _home = common::home_lock().await;
let fx = fixture_with_quota_on_agents(_home, &["alpha"], &["impl-A"]);
let mut runner = Runner::start(&fx.repo, "create note.txt".to_owned(), fx.config.clone(), magi::run::Origin::operator())
.await
.expect("start");
runner.execute().await.expect("execute");
let state = &runner.state;
assert!(
state.quota.is_empty(),
"a recovered seat must not leave a QuotaLoss behind: {:?}",
state.quota
);
let events: Vec<&str> = state
.events
.iter()
.filter(|e| e.node == "implement")
.map(|e| e.message.as_str())
.collect();
assert!(
events
.iter()
.any(|m| m.contains("rate limited (quota)") && m.contains("retrying with beta")),
"{events:?}"
);
let a = state
.candidates
.iter()
.find(|c| c.label == 'A')
.expect("candidate A");
assert!(!a.empty, "{a:?}");
assert!(a.failed.is_none(), "{a:?}");
assert!(a.commits > 0, "{a:?}");
assert_eq!(a.agent, "beta", "{a:?}");
}
}
common::e2e! {
async fn a_seat_that_exhausts_the_whole_roster_records_exactly_one_quota_loss() {
let _home = common::home_lock().await;
let fx = fixture_with_quota_on_agents(_home, &["alpha", "beta", "gamma"], &["impl-A"]);
let mut runner = Runner::start(&fx.repo, "create note.txt".to_owned(), fx.config.clone(), magi::run::Origin::operator())
.await
.expect("start");
let err = runner
.execute()
.await
.expect_err("no candidate produced a change");
assert!(
err.to_string().contains("no candidate produced a change"),
"{err}"
);
let state = &runner.state;
assert_eq!(state.quota.len(), 1, "{:?}", state.quota);
assert_eq!(state.quota[0].seat, "impl-A");
assert_eq!(state.quota[0].node, "implement");
let events: Vec<&str> = state
.events
.iter()
.filter(|e| e.node == "implement")
.map(|e| e.message.as_str())
.collect();
assert!(
events.iter().any(|m| m.contains("retrying with beta")),
"{events:?}"
);
assert!(
events.iter().any(|m| m.contains("retrying with gamma")),
"{events:?}"
);
let a = state
.candidates
.iter()
.find(|c| c.label == 'A')
.expect("candidate A");
assert!(a.empty, "{a:?}");
assert_eq!(a.commits, 0, "{a:?}");
assert_eq!(
a.failed.as_deref(),
Some("rate limited (quota); produced no change")
);
assert_eq!(a.agent, "alpha", "{a:?}");
}
}
common::e2e! {
async fn a_later_candidate_slots_seat_falls_back_past_its_own_position_not_the_roster_front() {
let _home = common::home_lock().await;
let mut fx = common::fixture(_home, common::Judges::Unanimous, false);
fx.config.graph.implementers = 2;
let labels = magi::blind::assign_labels(2, fx.config.blind.seed.expect("fixture pins a seed"));
let seat_for_slot_1 = format!("impl-{}", labels[1]);
for a in &mut fx.config.agents {
if a.id == "beta" {
a.env
.insert("MOCK_QUOTA_SEAT".to_owned(), seat_for_slot_1.clone());
}
}
let mut runner = Runner::start(&fx.repo, "create note.txt".to_owned(), fx.config.clone(), magi::run::Origin::operator())
.await
.expect("start");
runner.execute().await.expect("execute");
let state = &runner.state;
assert!(state.quota.is_empty(), "{:?}", state.quota);
let events: Vec<&str> = state
.events
.iter()
.filter(|e| e.node == "implement")
.map(|e| e.message.as_str())
.collect();
assert!(
events.iter().any(|m| m.contains("retrying with gamma")),
"{events:?}"
);
assert!(
!events.iter().any(|m| m.contains("retrying with alpha")),
"must not fall back onto a different candidate slot's own agent: {events:?}"
);
let slot0 = state
.candidates
.iter()
.find(|c| c.index == 0)
.expect("candidate slot 0");
assert_eq!(slot0.agent, "alpha", "{slot0:?}");
assert!(!slot0.empty && slot0.failed.is_none(), "{slot0:?}");
let slot1 = state
.candidates
.iter()
.find(|c| c.index == 1)
.expect("candidate slot 1");
assert_eq!(slot1.agent, "gamma", "{slot1:?}");
assert!(!slot1.empty && slot1.failed.is_none(), "{slot1:?}");
}
}
common::e2e! {
async fn a_later_candidate_slots_fallback_chain_stops_at_the_rosters_tail_without_wrapping() {
let _home = common::home_lock().await;
let mut fx = common::fixture(_home, common::Judges::Unanimous, false);
fx.config.graph.implementers = 2;
let labels = magi::blind::assign_labels(2, fx.config.blind.seed.expect("fixture pins a seed"));
let seat_for_slot_1 = format!("impl-{}", labels[1]);
for a in &mut fx.config.agents {
if a.id == "beta" || a.id == "gamma" {
a.env
.insert("MOCK_QUOTA_SEAT".to_owned(), seat_for_slot_1.clone());
}
}
let mut runner = Runner::start(&fx.repo, "create note.txt".to_owned(), fx.config.clone(), magi::run::Origin::operator())
.await
.expect("start");
runner.execute().await.expect("execute");
let state = &runner.state;
assert_eq!(state.quota.len(), 1, "{:?}", state.quota);
assert_eq!(state.quota[0].seat, seat_for_slot_1);
let events: Vec<&str> = state
.events
.iter()
.filter(|e| e.node == "implement")
.map(|e| e.message.as_str())
.collect();
assert!(
events.iter().any(|m| m.contains("retrying with gamma")),
"{events:?}"
);
assert!(
!events.iter().any(|m| m.contains("retrying with alpha")),
"must not wrap back onto a different candidate slot's own agent: {events:?}"
);
let slot0 = state
.candidates
.iter()
.find(|c| c.index == 0)
.expect("candidate slot 0");
assert_eq!(slot0.agent, "alpha", "{slot0:?}");
assert!(!slot0.empty && slot0.failed.is_none(), "{slot0:?}");
let slot1 = state
.candidates
.iter()
.find(|c| c.index == 1)
.expect("candidate slot 1");
assert!(slot1.empty, "{slot1:?}");
assert_eq!(
slot1.failed.as_deref(),
Some("rate limited (quota); produced no change")
);
assert_eq!(slot1.agent, "beta", "{slot1:?}");
}
}
common::e2e! {
async fn replacement_implementer_pin_refuses_quota_fallback_and_holds_task() {
let home = common::home_lock().await;
let mut fx = fixture_with_quota_on_agents(home, &["alpha"], &["impl-A"]);
fx.config.graph.selected_implementer = Some("alpha".into());
let mut runner = Runner::start(&fx.repo, "create note.txt".into(), fx.config.clone(), magi::run::Origin::operator()).await.unwrap();
let result = runner.execute().await;
assert!(result.is_err());
assert_eq!(runner.state.candidates.len(), 1);
assert_eq!(runner.state.candidates[0].agent, "alpha");
assert!(runner.state.handovers.is_empty(), "configured beta/gamma must not substitute for the pin");
assert_eq!(runner.state.quota.len(), 1);
let mut task = magi::queue::Task::new("Replacement".into(), "create note.txt".into(), fx.repo.clone(), magi::queue::Source::Human);
task.replacement = Some(magi::merge_decision::Replacement {
question: "old-question".into(), run: "old-run".into(), pr: "https://github.com/example/repo/pull/42".into(),
agent: "alpha".into(), context: "prior findings".into(),
});
task.start(runner.state.id.clone());
let queue = magi::queue::Queue::open();
queue.put(&mut task).unwrap();
magi::daemon::finish_attempt(3, &queue, &mut task, &runner.state, &[], result);
assert_eq!(task.status, magi::queue::TaskStatus::Held);
assert!(task.operator_held());
assert!(task.hold_reason.as_ref().unwrap().contains("alpha"));
assert!(task.hold_reason.as_ref().unwrap().contains("No other implementer"));
}
}
common::e2e! {
async fn selected_implementer_controls_initial_dispatch_and_saved_resume() {
let home = common::home_lock().await;
let mut fx = fixture_with_quota_on_agents(home, &["alpha"], &["impl-A"]);
fx.config.graph.selected_implementer = Some("beta".into());
let mut runner = Runner::start(&fx.repo, "create note.txt".into(), fx.config.clone(), magi::run::Origin::operator()).await.unwrap();
runner.execute().await.unwrap();
assert_eq!(runner.state.candidates.len(), 1);
assert_eq!(runner.state.candidates[0].agent, "beta");
assert!(runner.state.handovers.is_empty());
let mut disk = magi::run::RunState::load(&runner.state.id).unwrap();
assert_eq!(disk.config.graph.selected_implementer.as_deref(), Some("beta"));
disk.candidates[0].agent = "alpha".into();
disk.save().unwrap();
assert!(Runner::resume(&disk.id).is_err(), "a saved seat must not bypass the implementer pin");
}
}
common::e2e! {
async fn selected_implementer_quota_cannot_publish_a_seeded_partial_patch() {
let home = common::home_lock().await;
let mut fx = fixture_with_quota_on_agents(home, &["alpha"], &["impl-A"]);
let mut initial = fx.config.clone();
for agent in &mut initial.agents { agent.env.remove("MOCK_QUOTA_SEAT"); }
let mut old = Runner::start(&fx.repo, "create note.txt".into(), initial, magi::run::Origin::operator()).await.unwrap();
old.execute().await.unwrap();
let winner = old.state.winner().unwrap();
let head = magi::git::rev_parse(&winner.worktree, "HEAD").await.unwrap();
fx.config.graph.selected_implementer = Some("alpha".into());
let mut replacement = Runner::start(&fx.repo, format!("Reimplement {head}; create note.txt"), fx.config.clone(), magi::run::Origin::operator()).await.unwrap();
assert!(replacement.execute().await.is_err());
assert_eq!(replacement.state.status, magi::run::RunStatus::Blocked);
assert!(replacement.state.candidates[0].commits > 0, "the inherited patch alone cannot stand in for the selected implementer");
assert_eq!(replacement.state.quota.len(), 1);
assert!(replacement.state.reviews.is_empty());
assert!(replacement.state.pr.is_none());
assert!(replacement.state.handovers.is_empty());
}
}