70 lines
2.9 KiB
Rust
70 lines
2.9 KiB
Rust
|
|
//! Merge control — server-side trigger and failure reporting.
|
||
|
|
use crate::slog;
|
||
|
|
use crate::slog_error;
|
||
|
|
use crate::slog_warn;
|
||
|
|
use std::sync::Arc;
|
||
|
|
|
||
|
|
use super::super::super::AgentPool;
|
||
|
|
use crate::agents::{PipelineStage, pipeline_stage};
|
||
|
|
|
||
|
|
impl AgentPool {
|
||
|
|
/// Trigger a deterministic server-side merge for `story_id` without spawning
|
||
|
|
/// an LLM agent.
|
||
|
|
///
|
||
|
|
/// Constructs an `Arc<Self>` from the pool's shared fields and delegates to
|
||
|
|
/// [`start_merge_agent_work`]. The merge runs in a background task; this
|
||
|
|
/// function returns immediately.
|
||
|
|
pub(crate) fn trigger_server_side_merge(&self, project_root: &std::path::Path, story_id: &str) {
|
||
|
|
let pool = Arc::new(Self {
|
||
|
|
agents: Arc::clone(&self.agents),
|
||
|
|
port: self.port,
|
||
|
|
child_killers: Arc::clone(&self.child_killers),
|
||
|
|
watcher_tx: self.watcher_tx.clone(),
|
||
|
|
status_broadcaster: Arc::clone(&self.status_broadcaster),
|
||
|
|
});
|
||
|
|
if let Err(e) = pool.start_merge_agent_work(project_root, story_id) {
|
||
|
|
slog_error!("[merge] Failed to trigger server-side merge for '{story_id}': {e}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Record that the mergemaster agent for `story_id` explicitly reported a
|
||
|
|
/// merge failure via the `report_merge_failure` MCP tool.
|
||
|
|
///
|
||
|
|
/// Sets `merge_failure_reported = true` on the active mergemaster agent so
|
||
|
|
/// that `run_pipeline_advance` can block advancement to `5_done/` even when
|
||
|
|
/// the server-owned gate check returns `gates_passed=true` (those gates run
|
||
|
|
/// in the feature-branch worktree, not on master).
|
||
|
|
pub fn set_merge_failure_reported(&self, story_id: &str) {
|
||
|
|
match self.agents.lock() {
|
||
|
|
Ok(mut lock) => {
|
||
|
|
let found = lock.iter_mut().find(|(key, agent)| {
|
||
|
|
let key_story_id = key
|
||
|
|
.rsplit_once(':')
|
||
|
|
.map(|(sid, _)| sid)
|
||
|
|
.unwrap_or(key.as_str());
|
||
|
|
key_story_id == story_id
|
||
|
|
&& pipeline_stage(&agent.agent_name) == PipelineStage::Mergemaster
|
||
|
|
});
|
||
|
|
match found {
|
||
|
|
Some((_, agent)) => {
|
||
|
|
agent.merge_failure_reported = true;
|
||
|
|
slog!(
|
||
|
|
"[pipeline] Merge failure flag set for '{story_id}:{}'",
|
||
|
|
agent.agent_name
|
||
|
|
);
|
||
|
|
}
|
||
|
|
None => {
|
||
|
|
slog_warn!(
|
||
|
|
"[pipeline] set_merge_failure_reported: no running mergemaster found \
|
||
|
|
for story '{story_id}' — flag not set"
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Err(e) => {
|
||
|
|
slog_error!("[pipeline] set_merge_failure_reported: could not lock agents: {e}");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|