From dbb3afc049a37b37c679f60c5f15a0fa01a4f097 Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 06:23:03 +0000 Subject: [PATCH 1/3] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P6:=20host=5Fide?= =?UTF-8?q?ntity=5Fconverge=20drops=20its=20sudo-hostnamectl=20ShellCom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/host_effect.dag | 2 ++ dag/gunbc/host_effect_realize.dag | 33 ++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/dag/gunbc/host_effect.dag b/dag/gunbc/host_effect.dag index b3345ba052a..aa2d37144f5 100644 --- a/dag/gunbc/host_effect.dag +++ b/dag/gunbc/host_effect.dag @@ -5,6 +5,7 @@ import std.access { AccessDecision } import std.disposition { Disposition, RealizationDispatch, Scaffold, Terminal } import std.decl_ref { DeclarationRef, WholeDeclaration } import extdeps.bmc.types { RedfishSystemAction } +import extdeps.dns.domain_name { DnsLabel } import gunbc.fleet_intent { ComputeHost } import gunbc.host_converge { HostConverge } import std.effects { @@ -21,6 +22,7 @@ type HostEffect = ShellCommand { script: String } | RedfishAction { action: RedfishSystemAction } | ConvergePlan { policy: HostConverge } + | SetHostnameCas { hostnamectl_path: NonEmptyStr, base: DnsLabel, desired: DnsLabel } type NodeControlPlane = HostOs { node: ComputeHost } diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index b4cbf22cc69..2d74bfbf511 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -1,7 +1,7 @@ module gunbc.host_effect_realize import gunbc.host_effect { - HostEffect, ShellCommand, RedfishAction, ConvergePlan, + HostEffect, ShellCommand, RedfishAction, ConvergePlan, SetHostnameCas, NodeControlPlane, HostOs, BmcController, Drive, OneShot, ConvergeLoop, HostEffectIntent, HostEffectEvidence, ShellEffectApplied, ConvergePlanApplied, @@ -9,6 +9,7 @@ import gunbc.host_effect { EmitArtifactThenThinRun, BootstrapFragment, host_effect_drive, } +import extdeps.dns.domain_name { DnsLabel } import std.effects { IdempotencyEvidence } import std.realization_reconcile { Reconciliation, Converged, NotConverged, @@ -38,6 +39,7 @@ type ResolvedHostEffectCell = ShellOnHost { node: ComputeHost, script: String } | RedfishOnBmc { node: ComputeHost, action: RedfishSystemAction } | ConvergeOnHost { node: ComputeHost, host_converge: HostConverge } + | HostnameOnHost { node: ComputeHost, hostnamectl_path: NonEmptyStr, base: DnsLabel, desired: DnsLabel } | IncompatibleCell { reason: String } fn converge_plan_identity_mismatch_reason(node: ComputeHost, policy: HostConverge) -> String { @@ -63,6 +65,8 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res } else { ConvergeOnHost { node: n, host_converge: p } } + SetHostnameCas { hostnamectl_path: p, base: b, desired: d } => + HostnameOnHost { node: n, hostnamectl_path: p, base: b, desired: d } } BmcController { node: n } => match effect { @@ -71,6 +75,8 @@ fn resolve_host_effect_cell(target: NodeControlPlane, effect: HostEffect) -> Res RedfishAction { action: a } => RedfishOnBmc { node: n, action: a } ConvergePlan { policy: _ } => IncompatibleCell { reason: "host_effect: ConvergePlan is in-band (host OS converge) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } + SetHostnameCas { hostnamectl_path: _, base: _, desired: _ } => + IncompatibleCell { reason: "host_effect: SetHostnameCas is in-band (shell hostnamectl) and cannot target BmcController (out-of-band Redfish only); target HostOs instead. Typed mismatch via total fold, DESIGN section 5." } } } } @@ -186,6 +192,27 @@ fn realize_shell_on_host(script: String, transport: HostEffectTransport, intent: ) } +fn host_effect_set_hostname_cas_script(hostnamectl_path: NonEmptyStr, base: DnsLabel, desired: DnsLabel) -> String { + concat( + concat("[ \"$(hostname -s)\" = \"", concat(base as String, "\" ] && sudo -n ")), + concat(hostnamectl_path as String, concat(" set-hostname ", desired as String)) + ) +} + +fn realize_set_hostname_cas_on_host( + hostnamectl_path: NonEmptyStr, + base: DnsLabel, + desired: DnsLabel, + transport: HostEffectTransport, + intent: HostEffectIntent, +) -> Reconciliation { + realize_shell_on_host( + script: host_effect_set_hostname_cas_script(hostnamectl_path: hostnamectl_path, base: base, desired: desired), + transport: transport, + intent: intent, + ) +} + fn host_effect_apply(target: NodeControlPlane, effect: HostEffect, evidence: IdempotencyEvidence, transport: HostEffectTransport) -> Reconciliation { let intent = HostEffectIntent { target: target, @@ -198,6 +225,8 @@ fn host_effect_apply(target: NodeControlPlane, effect: HostEffect, evidence: Ide ShellOnHost { node: _, script: s } => realize_shell_on_host(script: s, transport: transport, intent: intent) ConvergeOnHost { node: _, host_converge: hc } => realize_converge_on_host(host_converge: hc, transport: transport, intent: intent) + HostnameOnHost { node: _, hostnamectl_path: p, base: b, desired: d } => + realize_set_hostname_cas_on_host(hostnamectl_path: p, base: b, desired: d, transport: transport, intent: intent) RedfishOnBmc { node: _, action: _ } => NotConverged { reason: "host_effect_apply: RedfishAction realization is a fail-closed Unimplemented stub in Phase A; neat-boar-71 realizes it over RedfishRest in Phase C. No plausible no-op for a power/boot action (DESIGN section 5).", applied: intent } IncompatibleCell { reason: why } => NotConverged { reason: why, applied: intent } @@ -236,6 +265,8 @@ fn host_effect_apply_gated( ShellOnHost { node: _, script: s } => realize_shell_on_host(script: s, transport: transport, intent: intent) ConvergeOnHost { node: _, host_converge: hc } => realize_converge_on_host(host_converge: hc, transport: transport, intent: intent) + HostnameOnHost { node: _, hostnamectl_path: p, base: b, desired: d } => + realize_set_hostname_cas_on_host(hostnamectl_path: p, base: b, desired: d, transport: transport, intent: intent) RedfishOnBmc { node: _, action: _ } => NotConverged { reason: "host_effect_apply: RedfishAction realization is a fail-closed Unimplemented stub in Phase A; neat-boar-71 realizes it over RedfishRest in Phase C. No plausible no-op for a power/boot action (DESIGN section 5).", applied: intent } IncompatibleCell { reason: why } => NotConverged { reason: why, applied: intent } From 23d94cfaf5631cf471fdd995cf945256d32dc7ed Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 06:33:42 +0000 Subject: [PATCH 2/3] =?UTF-8?q?WIP:=20Shell=E2=86=92dag=20P6:=20host=5Fide?= =?UTF-8?q?ntity=5Fconverge=20drops=20its=20sudo-hostnamectl=20ShellCom?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- dag/gunbc/host_effect_realize.dag | 1 + dag/gunbc/host_identity_converge.dag | 46 ++++++++++------------------ 2 files changed, 17 insertions(+), 30 deletions(-) diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 2d74bfbf511..603ab538e44 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -306,6 +306,7 @@ fn host_effect_drive_directive(r: Reconciliation Refused { reason: inner_why } ShellOnHost { node: _, script: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) ConvergeOnHost { node: _, host_converge: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) + HostnameOnHost { node: _, hostnamectl_path: _, base: _, desired: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) RedfishOnBmc { node: _, action: _ } => not_converged_directive(drive: host_effect_drive(intent: intent)) } } diff --git a/dag/gunbc/host_identity_converge.dag b/dag/gunbc/host_identity_converge.dag index 246c682bb2d..accea87ea83 100644 --- a/dag/gunbc/host_identity_converge.dag +++ b/dag/gunbc/host_identity_converge.dag @@ -1,8 +1,6 @@ module gunbc.host_identity_converge import std.types { String, NonEmptyStr, Bool } -import std.disposition { Disposition, Scaffold, SingleAuthority } -import std.decl_ref { DeclarationRef, WholeDeclaration } import std.access { AccessDecision, Deny } import extdeps.dns.domain_name { DnsLabel } import std.effects { @@ -14,7 +12,8 @@ import std.effects { LatticeEffect, } import gunbc.host_effect { - ShellCommand, + HostEffect, + SetHostnameCas, NodeControlPlane, HostEffectTransport, HostEffectIntent, @@ -54,26 +53,6 @@ import std.realization_reconcile { reconciliation_converged, } -data host_identity_set_hostname_script_scaffold: Disposition = Scaffold { - dissolves_to: SingleAuthority, - bind: DeclarationRef { - module_path: "gunbc.live_deploy.emit", - decl_name: "live_deploy_emit_shell_dissolution_trigger", - field: WholeDeclaration, - } -} - -fn host_identity_set_hostname_script(desired_hostname: DnsLabel) -> String { - concat(concat("sudo -n ", host_identity_hostnamectl_path), concat(" set-hostname ", desired_hostname as String)) -} - -fn host_identity_set_hostname_script_cas(base_hostname: DnsLabel, desired_hostname: DnsLabel) -> String { - concat( - concat("[ \"$(hostname -s)\" = \"", base_hostname as String, "\" ] && sudo -n "), - concat(host_identity_hostnamectl_path, " set-hostname ", desired_hostname as String) - ) -} - fn host_identity_precondition_verdict( base: DeployedIntentV0, observed: HostIdentityObservation, @@ -148,9 +127,16 @@ fn host_identity_plan_from_verdict( } } -fn host_identity_plan_effect_script(plan: HostIdentityPlan) -> String? { +fn host_identity_plan_effect(plan: HostIdentityPlan) -> HostEffect? { match plan { - ApplyPlan { base: b, desired: d } => Present { value: host_identity_set_hostname_script_cas(base_hostname: b, desired_hostname: d) } + ApplyPlan { base: b, desired: d } => + Present { + value: SetHostnameCas { + hostnamectl_path: host_identity_hostnamectl_path, + base: b, + desired: d, + } + } NoopPlan => none ConflictPlan { verdict: _ } => none } @@ -168,7 +154,7 @@ fn host_identity_apply_evidence() -> IdempotencyEvidence { fn host_identity_apply_denied( target: NodeControlPlane, - script: String, + effect: HostEffect, transport: HostEffectTransport, reason: String, ) -> Reconciliation { @@ -176,7 +162,7 @@ fn host_identity_apply_denied( reason: reason, applied: HostEffectIntent { target: target, - effect: ShellCommand { script: script }, + effect: effect, evidence: host_identity_apply_evidence(), transport: transport, privilege_denial: none, @@ -191,13 +177,13 @@ fn host_identity_apply_gated( transport: HostEffectTransport, fixture_actor: DeployAccessActor?, ) -> Reconciliation? { - match host_identity_plan_effect_script(plan: plan) { - Present { value: script } => + match host_identity_plan_effect(plan: plan) { + Present { value: effect } => Present { value: host_effect_apply_gated( access: access, target: target, - effect: ShellCommand { script: script }, + effect: effect, evidence: host_identity_apply_evidence(), transport: transport, fixture_actor: fixture_actor, From 3487c53c92bac227cb91d2c2628aa524ae69cddb Mon Sep 17 00:00:00 2001 From: Brian Searls Date: Tue, 14 Jul 2026 18:23:40 +0000 Subject: [PATCH 3/3] Relocate SetHostnameCas shell scaffold to host_effect_hostname_script Move concat-built hostnamectl transport out of host_effect_realize into a rostered Scaffold module (srv3_host_effect_script precedent); swap roster entry from host_identity_converge (now typed-only) to the new realize handler. Co-authored-by: Cursor --- dag/gunbc/host_effect_hostname_script.dag | 24 ++++++++++++++++++++ dag/gunbc/host_effect_realize.dag | 8 +------ src/v2/lens/medium_structure_containment.dag | 2 +- 3 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 dag/gunbc/host_effect_hostname_script.dag diff --git a/dag/gunbc/host_effect_hostname_script.dag b/dag/gunbc/host_effect_hostname_script.dag new file mode 100644 index 00000000000..000b6e47b7d --- /dev/null +++ b/dag/gunbc/host_effect_hostname_script.dag @@ -0,0 +1,24 @@ +module gunbc.host_effect_hostname_script + +import std.types { String, NonEmptyStr } +import extdeps.dns.domain_name { DnsLabel } +import std.disposition { Disposition, Scaffold, SingleAuthority } +import std.decl_ref { DeclarationRef, WholeDeclaration } + +data host_effect_hostname_script_dissolution_trigger: String = "🟡 dissolve-on: host_effect_hostname_script — concat-built bash CAS hostnamectl body for SetHostnameCas realize-layer transport (P6 Part 1; relocated from host_identity_converge scaffold); DISSOLVES WHEN bash-emit (#5828 / shell-emission slice0) realizes SetHostnameCas through typed argv invocations / grammar-emitted nodes and host_effect_set_hostname_cas_script retires without concat-built transport strings; retire medium_structure_exception_roster entry for dag/gunbc/host_effect_hostname_script.dag. Wave 4 residue (shell→dag census §1C category C)." + +data host_effect_hostname_script_scaffold: Disposition = Scaffold { + dissolves_to: SingleAuthority, + bind: DeclarationRef { + module_path: "gunbc.host_effect_hostname_script", + decl_name: "host_effect_hostname_script_dissolution_trigger", + field: WholeDeclaration, + } +} + +fn host_effect_set_hostname_cas_script(hostnamectl_path: NonEmptyStr, base: DnsLabel, desired: DnsLabel) -> String { + concat( + concat("[ \"$(hostname -s)\" = \"", concat(base as String, "\" ] && sudo -n ")), + concat(hostnamectl_path as String, concat(" set-hostname ", desired as String)) + ) +} diff --git a/dag/gunbc/host_effect_realize.dag b/dag/gunbc/host_effect_realize.dag index 7675c4d839a..5a0cdacdaa5 100644 --- a/dag/gunbc/host_effect_realize.dag +++ b/dag/gunbc/host_effect_realize.dag @@ -46,6 +46,7 @@ import gunbc.ci_deploy_access { deploy_access_decision_reason, } import gunbc.srv3_host_effect_script { srv3_host_effect_script_for } +import gunbc.host_effect_hostname_script { host_effect_set_hostname_cas_script } import std.types { String, NonEmptyStr, List } type ResolvedHostEffectCell = @@ -242,13 +243,6 @@ fn realize_shell_on_host(script: String, transport: HostEffectTransport, intent: ) } -fn host_effect_set_hostname_cas_script(hostnamectl_path: NonEmptyStr, base: DnsLabel, desired: DnsLabel) -> String { - concat( - concat("[ \"$(hostname -s)\" = \"", concat(base as String, "\" ] && sudo -n ")), - concat(hostnamectl_path as String, concat(" set-hostname ", desired as String)) - ) -} - fn realize_set_hostname_cas_on_host( hostnamectl_path: NonEmptyStr, base: DnsLabel, diff --git a/src/v2/lens/medium_structure_containment.dag b/src/v2/lens/medium_structure_containment.dag index c12befb1279..8d1c4123f9b 100644 --- a/src/v2/lens/medium_structure_containment.dag +++ b/src/v2/lens/medium_structure_containment.dag @@ -161,7 +161,7 @@ data medium_structure_exception_roster: List = [ "dag/gunbc/ci_deploy_target_host.dag", "dag/gunbc/host_identity_observation.dag", "dag/gunbc/fleet_show_effective_read.dag", - "dag/gunbc/host_identity_converge.dag", + "dag/gunbc/host_effect_hostname_script.dag", "dag/gunbc/host_identity_adopt.dag", "dag/gunbc/host_identity_assimilation.dag", "dag/gunbc/githooks_pre_push_emit.dag",