From 600bc051e193c84dbfa4170a7d5c0079a5abe3e2 Mon Sep 17 00:00:00 2001 From: atlas Date: Mon, 20 Jul 2026 23:21:46 +0200 Subject: [PATCH] refactor(#2591): move the perm-change payload onto the WritePermFile node --- hive-c0re/src/job_queue/exec.rs | 16 ++++++++-------- hive-c0re/src/job_queue/mod.rs | 7 ------- hive-c0re/src/job_queue/model.rs | 16 ++++++++-------- hive-c0re/src/job_queue/submit.rs | 1 - hive-c0re/src/job_queue/templates.rs | 7 +------ hive-c0re/src/job_queue/tests.rs | 1 - hive-c0re/src/workers/auto_update.rs | 1 - hive-sh4re/src/jobs.rs | 2 -- hivectl/src/dag_progress.rs | 2 -- 9 files changed, 17 insertions(+), 36 deletions(-) diff --git a/hive-c0re/src/job_queue/exec.rs b/hive-c0re/src/job_queue/exec.rs index 86ab6994..3a232a3b 100644 --- a/hive-c0re/src/job_queue/exec.rs +++ b/hive-c0re/src/job_queue/exec.rs @@ -540,26 +540,30 @@ async fn run_write_perm_file( ) -> Result { use super::model::PermPayload; let name = &claim.agent; + // The perm file payload rides the node itself (the only consumer). + let NodeKind::WritePermFile { payload, .. } = &claim.kind else { + anyhow::bail!("run_write_perm_file on a non-WritePermFile node"); + }; ctx.step("writing + committing perm file"); // Deploy-window gate: a perm commit landing inside another node's // staged prepare→finalize window would sweep the staged deploy // lock into its commit (the commits are also path-limited in // meta.rs — belt and braces). let _window = crate::meta::exclusive().await; - match &claim.perm_payload { - Some(PermPayload::ToolGroups { groups }) => { + match payload { + PermPayload::ToolGroups { groups } => { crate::meta::commit_tool_groups(name, groups) .await .with_context(|| format!("commit tool-groups for {name}"))?; coord.emit_tool_groups_snapshot(); } - Some(PermPayload::Capabilities { caps }) => { + PermPayload::Capabilities { caps } => { crate::meta::commit_capabilities(name, caps) .await .with_context(|| format!("commit capabilities for {name}"))?; coord.emit_capabilities_snapshot(); } - Some(PermPayload::Combined { groups, caps }) => { + PermPayload::Combined { groups, caps } => { crate::meta::commit_perms(name, groups.as_deref(), caps.as_deref()) .await .with_context(|| format!("commit perms for {name}"))?; @@ -570,10 +574,6 @@ async fn run_write_perm_file( coord.emit_capabilities_snapshot(); } } - None => anyhow::bail!( - "perm_change dag {} for {name} is missing perm_payload", - claim.dag_id - ), } Ok(NodeOutput::default()) } diff --git a/hive-c0re/src/job_queue/mod.rs b/hive-c0re/src/job_queue/mod.rs index bf6158be..1b9fb040 100644 --- a/hive-c0re/src/job_queue/mod.rs +++ b/hive-c0re/src/job_queue/mod.rs @@ -76,7 +76,6 @@ pub struct Claim { pub template: Template, pub approval_id: Option, pub inputs: Vec, - pub perm_payload: Option, /// Transient pill kind for the lease window (from the spec). Whether the /// pill is currently shown is derived from live lease ownership /// ([`JobQueue::held_transients`]), not a per-claim edge. @@ -118,7 +117,6 @@ struct DagMeta { transient: Option, approval_id: Option, inputs: Vec, - perm_payload: Option, created_at: i64, } @@ -297,7 +295,6 @@ impl JobQueue { transient: spec.transient, approval_id: spec.approval_id, inputs: spec.inputs, - perm_payload: spec.perm_payload, created_at: now_unix(), }, Vec::new(), @@ -387,7 +384,6 @@ impl JobQueue { template: meta.template, approval_id: meta.approval_id, inputs: meta.inputs, - perm_payload: meta.perm_payload, transient: meta.transient, }); if let Some(rt) = inner.node_rt.get_mut(&id) { @@ -638,7 +634,6 @@ impl QueueInner { transient, approval_id, inputs, - perm_payload, created_at, } = &self.sched.graph().node(container)?.payload else { @@ -651,7 +646,6 @@ impl QueueInner { transient: *transient, approval_id: *approval_id, inputs: inputs.clone(), - perm_payload: perm_payload.clone(), created_at: *created_at, }) } @@ -801,7 +795,6 @@ impl QueueInner { }, inputs: meta.inputs.clone(), approval_id: meta.approval_id, - perm_payload: meta.perm_payload.clone(), nodes, }) } diff --git a/hive-c0re/src/job_queue/model.rs b/hive-c0re/src/job_queue/model.rs index 9c4da05c..bfc66aa0 100644 --- a/hive-c0re/src/job_queue/model.rs +++ b/hive-c0re/src/job_queue/model.rs @@ -119,9 +119,10 @@ pub enum NodeKind { Drain { agent: String }, /// `set_nspawn_flags` + `set_resource_limits` + daemon-reload. WriteDropin { agent: String }, - /// Commit `tool-groups.json` / `capabilities.json` per the DAG's - /// `perm_payload` (commit fused under `META_LOCK`). - WritePermFile { agent: String }, + /// Commit `tool-groups.json` / `capabilities.json` per its `payload` + /// (commit fused under `META_LOCK`). The payload rides this node — the only + /// consumer — rather than the generic DAG container. + WritePermFile { agent: String, payload: PermPayload }, /// Opaque approval deploy pipeline (`MergeConfigPr`): the two-phase /// prepare/finalize/abort meta deploy stays inside `actions.rs` in v1 — /// deliberately not @@ -156,7 +157,6 @@ pub enum NodeKind { transient: Option, approval_id: Option, inputs: Vec, - perm_payload: Option, created_at: i64, }, } @@ -203,7 +203,7 @@ impl NodeKind { | NodeKind::Signal { agent } | NodeKind::Drain { agent } | NodeKind::WriteDropin { agent } - | NodeKind::WritePermFile { agent } + | NodeKind::WritePermFile { agent, .. } | NodeKind::ApprovalDeploy { agent } | NodeKind::SetWanted { agent, .. } => agent, NodeKind::MetaLock { .. } | NodeKind::Dag { .. } => "", @@ -269,7 +269,9 @@ pub struct NodeSpec { /// Submit-time spec for a whole DAG. Built by `templates.rs`; validated /// (cycle rejection) by `JobQueue::submit`. No DAG-level `agent` — every /// node carries its own (a DAG can span agents), and the queue derives -/// per-agent leasing from [`NodeSpec::agent`]. +/// per-agent leasing from [`NodeKind::agent`]. Type-specific payloads +/// (`PermChange`'s file payload) ride the node that consumes them +/// ([`NodeKind::WritePermFile`]), not this generic spec. #[derive(Debug, Clone)] pub struct DagSpec { pub template: Template, @@ -281,8 +283,6 @@ pub struct DagSpec { /// `MetaUpdate`-only: the inputs to bump (also part of the dedup /// key for that template). Display copy lives on the DAG. pub inputs: Vec, - /// `PermChange`-only payload. - pub perm_payload: Option, /// Dashboard transient pill (and crash-watch suppression) held for /// the lease window — from lease acquisition to DAG terminal. pub transient: Option, diff --git a/hive-c0re/src/job_queue/submit.rs b/hive-c0re/src/job_queue/submit.rs index 5c0ddf4b..b4d85415 100644 --- a/hive-c0re/src/job_queue/submit.rs +++ b/hive-c0re/src/job_queue/submit.rs @@ -201,7 +201,6 @@ fn power_dag( reason, approval_id: None, inputs: Vec::new(), - perm_payload: None, transient: Some(transient), nodes, } diff --git a/hive-c0re/src/job_queue/templates.rs b/hive-c0re/src/job_queue/templates.rs index 5c739962..f456da8e 100644 --- a/hive-c0re/src/job_queue/templates.rs +++ b/hive-c0re/src/job_queue/templates.rs @@ -125,7 +125,6 @@ pub fn rebuild(agent: &str, source: Source, reason: String, relock: bool) -> Dag reason, approval_id: None, inputs: Vec::new(), - perm_payload: None, transient: Some(TransientKind::Rebuilding), nodes: rebuild_nodes(agent, relock, 0), } @@ -141,7 +140,6 @@ pub fn approval_deploy(agent: &str, approval_id: i64, reason: String) -> DagSpec reason, approval_id: Some(approval_id), inputs: Vec::new(), - perm_payload: None, transient: Some(TransientKind::Rebuilding), nodes: vec![node( NodeKind::ApprovalDeploy { @@ -171,7 +169,6 @@ pub fn reconcile_only( reason, approval_id: None, inputs: Vec::new(), - perm_payload: None, transient, nodes: vec![node( NodeKind::Reconcile { @@ -198,7 +195,6 @@ pub fn spawn(agent: &str, approval_id: i64, reason: String) -> DagSpec { reason, approval_id: Some(approval_id), inputs: Vec::new(), - perm_payload: None, transient: Some(TransientKind::Spawning), nodes: { let a = || agent.to_owned(); @@ -219,6 +215,7 @@ pub fn perm_change(agent: &str, source: Source, reason: String, payload: PermPay let mut nodes = vec![node( NodeKind::WritePermFile { agent: agent.to_owned(), + payload, }, Vec::new(), )]; @@ -229,7 +226,6 @@ pub fn perm_change(agent: &str, source: Source, reason: String, payload: PermPay reason, approval_id: None, inputs: Vec::new(), - perm_payload: Some(payload), transient: Some(TransientKind::Rebuilding), nodes, } @@ -257,7 +253,6 @@ pub fn meta_update( reason, approval_id, inputs, - perm_payload: None, transient: Some(TransientKind::Rebuilding), nodes: vec![node( NodeKind::MetaLock { diff --git a/hive-c0re/src/job_queue/tests.rs b/hive-c0re/src/job_queue/tests.rs index b70fc195..b385e420 100644 --- a/hive-c0re/src/job_queue/tests.rs +++ b/hive-c0re/src/job_queue/tests.rs @@ -561,7 +561,6 @@ fn append_subgraph_roots_on_emitter_and_rebases_local_deps() { reason: "sweep".to_owned(), approval_id: None, inputs: Vec::new(), - perm_payload: None, transient: None, nodes: vec![NodeSpec { kind: NodeKind::MetaLock { diff --git a/hive-c0re/src/workers/auto_update.rs b/hive-c0re/src/workers/auto_update.rs index 2ed340e1..8db07795 100644 --- a/hive-c0re/src/workers/auto_update.rs +++ b/hive-c0re/src/workers/auto_update.rs @@ -348,7 +348,6 @@ fn submit_boot_tree( reason, approval_id: None, inputs: Vec::new(), - perm_payload: None, // Rebuilding when the sweep will grow rebuild subgraphs (per-agent // crash-watch suppression during their Swap, applied at claim time); // a reconcile-only boot needs no transient. diff --git a/hive-sh4re/src/jobs.rs b/hive-sh4re/src/jobs.rs index cb5ef9b3..a429b72b 100644 --- a/hive-sh4re/src/jobs.rs +++ b/hive-sh4re/src/jobs.rs @@ -195,7 +195,5 @@ pub struct DagView { pub inputs: Vec, #[serde(default, skip_serializing_if = "Option::is_none")] pub approval_id: Option, - #[serde(default, skip_serializing_if = "Option::is_none")] - pub perm_payload: Option, pub nodes: Vec, } diff --git a/hivectl/src/dag_progress.rs b/hivectl/src/dag_progress.rs index 74c77d2d..c726773f 100644 --- a/hivectl/src/dag_progress.rs +++ b/hivectl/src/dag_progress.rs @@ -353,7 +353,6 @@ mod tests { finished_at: None, inputs: vec![], approval_id: None, - perm_payload: None, nodes: vec![ node(0, "alice", "prebuild", State::Done, None), node(1, "alice", "stop_for_update", State::Done, None), @@ -392,7 +391,6 @@ mod tests { finished_at: Some(2), inputs: vec![], approval_id: None, - perm_payload: None, nodes: vec![failed], }; let line = render_dag_line(&dag);