fix(#1218): bulk topology move uses one git commit via new set-parent-bulk endpoint

This commit is contained in:
damocles 2026-06-03 22:57:40 +02:00 committed by mara
commit 7ec0a36d7a
4 changed files with 179 additions and 4 deletions

View file

@ -78,6 +78,7 @@ pub async fn serve(port: u16, coord: Arc<Coordinator>) -> Result<()> {
.route("/retry-reminder/{id}", post(post_retry_reminder))
.route("/request-spawn", post(post_request_spawn))
.route("/api/topology/set-parent", post(post_set_parent))
.route("/api/topology/set-parent-bulk", post(post_set_parent_bulk))
.route("/api/tool-groups", get(get_tool_groups))
.route("/api/tool-groups/{agent}", post(post_tool_groups))
.route("/api/capabilities", get(get_capabilities))
@ -1054,6 +1055,15 @@ struct SetParentForm {
new_parent: Option<String>,
}
/// One entry in a `POST /api/topology/set-parent-bulk` JSON array.
/// `new_parent`: absent/null/empty-string all mean "promote to root".
#[derive(Deserialize)]
struct SetParentBulkEntry {
child: String,
#[serde(default)]
new_parent: Option<String>,
}
#[derive(Deserialize)]
struct AnswerForm {
answer: String,
@ -2476,6 +2486,36 @@ async fn post_set_parent(
}
}
/// `POST /api/topology/set-parent-bulk` — move multiple agents in a single
/// request, producing **one** git commit. JSON body: `[{"child":"name",
/// "new_parent":"target-or-null"}, ...]`. Empty array is a no-op (200 OK).
/// First validation error aborts the whole batch.
async fn post_set_parent_bulk(
State(state): State<AppState>,
axum::Json(body): axum::Json<Vec<SetParentBulkEntry>>,
) -> Response {
if body.is_empty() {
return (StatusCode::OK, "ok").into_response();
}
// Collect borrows for the coordinator call.
let moves: Vec<(&str, Option<&str>)> = body
.iter()
.map(|e| {
let child: &str = &e.child;
let parent: Option<&str> = e.new_parent.as_deref().filter(|s| !s.is_empty());
(child, parent)
})
.collect();
match state.coord.reparent_bulk_with_notify(&moves).await {
Ok(()) => {
let names: Vec<&str> = body.iter().map(|e| e.child.as_str()).collect();
tracing::info!(agents = ?names, "operator: set-parent-bulk via dashboard");
(StatusCode::OK, "ok").into_response()
}
Err(e) => error_response(&format!("set-parent-bulk failed: {e}")),
}
}
// ── tool-group endpoints ──────────────────────────────────
#[derive(Serialize)]