refactor: split long functions per review feedback; remove all #[allow] attributes
This commit is contained in:
parent
bbe2112dc9
commit
748536203b
5 changed files with 429 additions and 432 deletions
|
|
@ -23,7 +23,6 @@ use crate::lifecycle::{self, MANAGER_NAME};
|
|||
///
|
||||
/// In all cases an `ApprovalResolved` helper event lands in the manager's
|
||||
/// inbox when the work resolves.
|
||||
#[allow(clippy::too_many_lines)] // approval dispatch covers several independent approval kinds
|
||||
pub async fn approve(coord: Arc<Coordinator>, id: i64) -> Result<()> {
|
||||
let approval = coord.approvals.mark_approved(id)?;
|
||||
tracing::info!(
|
||||
|
|
@ -33,142 +32,164 @@ pub async fn approve(coord: Arc<Coordinator>, id: i64) -> Result<()> {
|
|||
%approval.commit_ref,
|
||||
"approval: running action",
|
||||
);
|
||||
|
||||
let agent_dir = coord.ensure_runtime(&approval.agent)?;
|
||||
let proposed_dir = Coordinator::agent_proposed_dir(&approval.agent);
|
||||
let applied_dir = Coordinator::agent_applied_dir(&approval.agent);
|
||||
let claude_dir = Coordinator::agent_claude_dir(&approval.agent);
|
||||
let notes_dir = Coordinator::agent_notes_dir(&approval.agent);
|
||||
|
||||
match approval.kind {
|
||||
ApprovalKind::ApplyCommit => {
|
||||
let (result, terminal_tag, is_first_spawn) = run_apply_commit(
|
||||
&coord,
|
||||
&approval,
|
||||
&agent_dir,
|
||||
&applied_dir,
|
||||
&claude_dir,
|
||||
¬es_dir,
|
||||
)
|
||||
.await;
|
||||
// Mirror the applied repo's new tag/branch state (approved/
|
||||
// building/deployed-or-failed + main) to the forge.
|
||||
if let Err(e) = crate::forge::push_config(&approval.agent).await {
|
||||
tracing::warn!(agent = %approval.agent, error = ?e, "forge: push_config after apply failed");
|
||||
}
|
||||
if is_first_spawn && result.is_ok() {
|
||||
// First-spawn bookkeeping: create the per-agent forge user,
|
||||
// mirror the applied repo into agent-configs/<n>, and grant
|
||||
// read access to core/meta.
|
||||
if let Err(e) = crate::forge::ensure_user_for(&approval.agent).await {
|
||||
tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_user after first spawn failed");
|
||||
}
|
||||
if let Err(e) = crate::forge::ensure_config_repo(&approval.agent).await {
|
||||
tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_config_repo after first spawn failed");
|
||||
}
|
||||
if let Some(core_token) = crate::forge::core_token()
|
||||
&& let Err(e) = crate::forge::meta_read_access(&approval.agent, &core_token).await {
|
||||
tracing::warn!(agent = %approval.agent, error = ?e, "forge: meta_read_access after first spawn failed");
|
||||
}
|
||||
if let Err(e) = crate::forge::ensure_meta_remote(&approval.agent).await {
|
||||
tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_meta_remote after first spawn failed");
|
||||
}
|
||||
// New container row appeared — rescan so the dashboard
|
||||
// reflects the post-spawn state without a manual refetch.
|
||||
coord.rescan_containers_and_emit().await;
|
||||
crate::dashboard::emit_tombstones_snapshot(&coord).await;
|
||||
}
|
||||
finish_approval(&coord, &approval, result, terminal_tag, is_first_spawn)
|
||||
approve_apply_commit(coord, approval, agent_dir, applied_dir, claude_dir, notes_dir).await
|
||||
}
|
||||
ApprovalKind::InitConfig => {
|
||||
// Seed the proposed config repo. Runs synchronously — it's just
|
||||
// a few git operations with no nixos-container involvement.
|
||||
let result: Result<()> = async {
|
||||
lifecycle::setup_proposed(&proposed_dir, &approval.agent).await?;
|
||||
lifecycle::ensure_claude_dir(&claude_dir)?;
|
||||
lifecycle::ensure_state_dir(¬es_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
.await;
|
||||
// Wire the meta remote now that the proposed repo exists.
|
||||
if result.is_ok()
|
||||
&& let Err(e) = crate::forge::ensure_meta_remote(&approval.agent).await {
|
||||
tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_meta_remote after init_config failed");
|
||||
}
|
||||
finish_approval(&coord, &approval, result, None, false)
|
||||
}
|
||||
ApprovalKind::UpdateMetaInputs => {
|
||||
// Decode the inputs from the commit_ref field (stored as JSON
|
||||
// by submit_apply_commit's counterpart in manager_server.rs).
|
||||
let inputs: Vec<String> =
|
||||
serde_json::from_str(&approval.commit_ref).unwrap_or_default();
|
||||
let result = crate::meta::lock_update(&inputs).await;
|
||||
finish_approval(&coord, &approval, result, None, false)
|
||||
approve_init_config(coord, approval, proposed_dir, claude_dir, notes_dir).await
|
||||
}
|
||||
ApprovalKind::UpdateMetaInputs => approve_update_meta_inputs(coord, approval).await,
|
||||
ApprovalKind::Spawn => {
|
||||
// Run the spawn in the background so the approve POST returns
|
||||
// immediately. The dashboard reads `transient` to render a spinner.
|
||||
// Guard is created synchronously here (so the spinner appears
|
||||
// the moment the operator clicks approve) and moved into the
|
||||
// task; it auto-clears even if the runtime drops the task.
|
||||
let coord_bg = coord.clone();
|
||||
let approval_bg = approval.clone();
|
||||
let guard = coord_bg.transient_guard(&approval_bg.agent, TransientKind::Spawning);
|
||||
tokio::spawn(async move {
|
||||
let guard = guard;
|
||||
let agent_bg = approval_bg.agent.clone();
|
||||
let result = lifecycle::spawn(
|
||||
&approval_bg.agent,
|
||||
&coord_bg.hyperhive_flake,
|
||||
&agent_dir,
|
||||
&proposed_dir,
|
||||
&applied_dir,
|
||||
&claude_dir,
|
||||
¬es_dir,
|
||||
coord_bg.dashboard_port,
|
||||
&coord_bg.operator_pronouns,
|
||||
&coord_bg.context_window_tokens,
|
||||
)
|
||||
.await;
|
||||
drop(guard);
|
||||
if result.is_ok() {
|
||||
if let Err(e) = crate::forge::ensure_user_for(&agent_bg).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: ensure_user after spawn failed");
|
||||
}
|
||||
// Create the agent-configs mirror repo and seed it
|
||||
// with the freshly-initialised applied repo (main +
|
||||
// deployed/0).
|
||||
if let Err(e) = crate::forge::ensure_config_repo(&agent_bg).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: ensure_config_repo after spawn failed");
|
||||
}
|
||||
if let Err(e) = crate::forge::push_config(&agent_bg).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: push_config after spawn failed");
|
||||
}
|
||||
if let Some(core_token) = crate::forge::core_token()
|
||||
&& let Err(e) = crate::forge::meta_read_access(&agent_bg, &core_token).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: meta_read_access after spawn failed");
|
||||
}
|
||||
if let Err(e) = crate::forge::ensure_meta_remote(&agent_bg).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: ensure_meta_remote after spawn failed");
|
||||
}
|
||||
}
|
||||
if let Err(e) = finish_approval(&coord_bg, &approval_bg, result, None, false) {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "spawn approval failed");
|
||||
}
|
||||
// New container row appeared (or didn't, on failure
|
||||
// before nixos-container create completed) — rescan so
|
||||
// dashboards reflect the post-spawn state. Spawn can
|
||||
// also consume a tombstone of the same name; emit the
|
||||
// fresh list so the operator's dormant-state pane
|
||||
// updates without a refetch.
|
||||
coord_bg.rescan_containers_and_emit().await;
|
||||
crate::dashboard::emit_tombstones_snapshot(&coord_bg).await;
|
||||
});
|
||||
approve_spawn(&coord, &approval, agent_dir, proposed_dir, applied_dir, claude_dir, notes_dir);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn approve_apply_commit(
|
||||
coord: Arc<Coordinator>,
|
||||
approval: hive_sh4re::Approval,
|
||||
agent_dir: std::path::PathBuf,
|
||||
applied_dir: std::path::PathBuf,
|
||||
claude_dir: std::path::PathBuf,
|
||||
notes_dir: std::path::PathBuf,
|
||||
) -> Result<()> {
|
||||
let (result, terminal_tag, is_first_spawn) = run_apply_commit(
|
||||
&coord,
|
||||
&approval,
|
||||
&agent_dir,
|
||||
&applied_dir,
|
||||
&claude_dir,
|
||||
¬es_dir,
|
||||
)
|
||||
.await;
|
||||
if let Err(e) = crate::forge::push_config(&approval.agent).await {
|
||||
tracing::warn!(agent = %approval.agent, error = ?e, "forge: push_config after apply failed");
|
||||
}
|
||||
if is_first_spawn && result.is_ok() {
|
||||
forge_after_first_spawn(&coord, &approval.agent).await;
|
||||
}
|
||||
finish_approval(&coord, &approval, result, terminal_tag, is_first_spawn)
|
||||
}
|
||||
|
||||
/// Forge bookkeeping run once after the very first container spawn:
|
||||
/// create the per-agent forge user, mirror the applied repo, and grant
|
||||
/// read access to core/meta. Also rescans containers so the dashboard
|
||||
/// reflects the post-spawn state.
|
||||
async fn forge_after_first_spawn(coord: &Arc<Coordinator>, agent: &str) {
|
||||
if let Err(e) = crate::forge::ensure_user_for(agent).await {
|
||||
tracing::warn!(%agent, error = ?e, "forge: ensure_user after first spawn failed");
|
||||
}
|
||||
if let Err(e) = crate::forge::ensure_config_repo(agent).await {
|
||||
tracing::warn!(%agent, error = ?e, "forge: ensure_config_repo after first spawn failed");
|
||||
}
|
||||
if let Some(core_token) = crate::forge::core_token()
|
||||
&& let Err(e) = crate::forge::meta_read_access(agent, &core_token).await {
|
||||
tracing::warn!(%agent, error = ?e, "forge: meta_read_access after first spawn failed");
|
||||
}
|
||||
if let Err(e) = crate::forge::ensure_meta_remote(agent).await {
|
||||
tracing::warn!(%agent, error = ?e, "forge: ensure_meta_remote after first spawn failed");
|
||||
}
|
||||
coord.rescan_containers_and_emit().await;
|
||||
crate::dashboard::emit_tombstones_snapshot(coord).await;
|
||||
}
|
||||
|
||||
async fn approve_init_config(
|
||||
coord: Arc<Coordinator>,
|
||||
approval: hive_sh4re::Approval,
|
||||
proposed_dir: std::path::PathBuf,
|
||||
claude_dir: std::path::PathBuf,
|
||||
notes_dir: std::path::PathBuf,
|
||||
) -> Result<()> {
|
||||
// Seed the proposed config repo — just git operations, no nixos-container.
|
||||
let result: Result<()> = async {
|
||||
lifecycle::setup_proposed(&proposed_dir, &approval.agent).await?;
|
||||
lifecycle::ensure_claude_dir(&claude_dir)?;
|
||||
lifecycle::ensure_state_dir(¬es_dir)?;
|
||||
Ok(())
|
||||
}
|
||||
.await;
|
||||
if result.is_ok()
|
||||
&& let Err(e) = crate::forge::ensure_meta_remote(&approval.agent).await {
|
||||
tracing::warn!(agent = %approval.agent, error = ?e, "forge: ensure_meta_remote after init_config failed");
|
||||
}
|
||||
finish_approval(&coord, &approval, result, None, false)
|
||||
}
|
||||
|
||||
async fn approve_update_meta_inputs(
|
||||
coord: Arc<Coordinator>,
|
||||
approval: hive_sh4re::Approval,
|
||||
) -> Result<()> {
|
||||
// Inputs stored as JSON in commit_ref by the manager's submit path.
|
||||
let inputs: Vec<String> = serde_json::from_str(&approval.commit_ref).unwrap_or_default();
|
||||
let result = crate::meta::lock_update(&inputs).await;
|
||||
finish_approval(&coord, &approval, result, None, false)
|
||||
}
|
||||
|
||||
fn approve_spawn(
|
||||
coord: &Arc<Coordinator>,
|
||||
approval: &hive_sh4re::Approval,
|
||||
agent_dir: std::path::PathBuf,
|
||||
proposed_dir: std::path::PathBuf,
|
||||
applied_dir: std::path::PathBuf,
|
||||
claude_dir: std::path::PathBuf,
|
||||
notes_dir: std::path::PathBuf,
|
||||
) {
|
||||
// Run spawn in the background so approve POST returns immediately.
|
||||
// Guard created synchronously so the spinner appears the moment
|
||||
// the operator clicks approve; auto-clears when the task drops it.
|
||||
let coord_bg = Arc::clone(coord);
|
||||
let approval_bg = approval.clone();
|
||||
let guard = coord_bg.transient_guard(&approval_bg.agent, TransientKind::Spawning);
|
||||
tokio::spawn(async move {
|
||||
let guard = guard;
|
||||
let agent_bg = approval_bg.agent.clone();
|
||||
let result = lifecycle::spawn(
|
||||
&approval_bg.agent,
|
||||
&coord_bg.hyperhive_flake,
|
||||
&agent_dir,
|
||||
&proposed_dir,
|
||||
&applied_dir,
|
||||
&claude_dir,
|
||||
¬es_dir,
|
||||
coord_bg.dashboard_port,
|
||||
&coord_bg.operator_pronouns,
|
||||
&coord_bg.context_window_tokens,
|
||||
)
|
||||
.await;
|
||||
drop(guard);
|
||||
if result.is_ok() {
|
||||
if let Err(e) = crate::forge::ensure_user_for(&agent_bg).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: ensure_user after spawn failed");
|
||||
}
|
||||
if let Err(e) = crate::forge::ensure_config_repo(&agent_bg).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: ensure_config_repo after spawn failed");
|
||||
}
|
||||
if let Err(e) = crate::forge::push_config(&agent_bg).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: push_config after spawn failed");
|
||||
}
|
||||
if let Some(core_token) = crate::forge::core_token()
|
||||
&& let Err(e) = crate::forge::meta_read_access(&agent_bg, &core_token).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: meta_read_access after spawn failed");
|
||||
}
|
||||
if let Err(e) = crate::forge::ensure_meta_remote(&agent_bg).await {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "forge: ensure_meta_remote after spawn failed");
|
||||
}
|
||||
}
|
||||
if let Err(e) = finish_approval(&coord_bg, &approval_bg, result, None, false) {
|
||||
tracing::warn!(agent = %agent_bg, error = ?e, "spawn approval failed");
|
||||
}
|
||||
coord_bg.rescan_containers_and_emit().await;
|
||||
crate::dashboard::emit_tombstones_snapshot(&coord_bg).await;
|
||||
});
|
||||
}
|
||||
|
||||
fn finish_approval(
|
||||
coord: &Coordinator,
|
||||
approval: &hive_sh4re::Approval,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue