From ed48185fb1bf030a69e28d8095bc8c0ce2e1ed4c Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Sat, 18 Jul 2026 20:32:56 +0100 Subject: [PATCH] Supervise Bifrost Persona feedback deployment --- crates/idunn-daemon/src/main.rs | 72 ++++++++++++++++++- ...loy-yggdrasil-bifrost-persona-feedback.ps1 | 14 ++++ 2 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 scripts/deploy-yggdrasil-bifrost-persona-feedback.ps1 diff --git a/crates/idunn-daemon/src/main.rs b/crates/idunn-daemon/src/main.rs index a1b5010..79c0f46 100644 --- a/crates/idunn-daemon/src/main.rs +++ b/crates/idunn-daemon/src/main.rs @@ -1390,7 +1390,16 @@ fn missing_daemon_published_health( desired: &IdunnDesiredDaemonRecord, observed_at: &str, ) -> IdunnDaemonHealthRecord { - let (state, detail, transport) = if target.health_contract.restart_on_missing_publication { + let (state, detail, transport) = if target.health_contract.default_failure_state == "stale-deployment" { + ( + "stale-deployment", + format!( + "no fresh daemon-published {} record arrived over {}; Idunn owns this declared deployment boundary.", + desired.health_contract, CULTNET_RUDP_PROTOCOL_ID + ), + "cultmesh.missing-deployment-health-publication", + ) + } else if target.health_contract.restart_on_missing_publication { ( "failed", format!( @@ -3107,6 +3116,20 @@ fn swarm_targets(options: &SwarmOptions) -> Result> { enabled: true, interval_seconds: 300, }, + DaemonTarget { + daemon_id: "yggdrasil-bifrost-persona-feedback".to_string(), + verse_id: "yggdrasil.local".to_string(), + name: "Yggdrasil Bifrost Persona feedback".to_string(), + health_contract: health_contract( + "bifrost.cultnet-rudp-persona-feedback-health", + "stale-deployment", + ), + deploy_command: Some(script("deploy-yggdrasil-bifrost-persona-feedback.ps1")), + restart_command: None, + release: None, + enabled: true, + interval_seconds: 30, + }, DaemonTarget { daemon_id: "yggdrasil-repixelizer".to_string(), verse_id: "yggdrasil.local".to_string(), @@ -5376,6 +5399,53 @@ mod tests { } } + #[test] + fn bifrost_persona_feedback_target_uses_daemon_health_and_idunn_deployment() { + let options = SwarmOptions { + profile: "starfire-local".to_string(), + repo_root: PathBuf::from("E:/Projects/Odin"), + }; + let targets = swarm_targets(&options).expect("starfire-local targets"); + let target = targets + .iter() + .find(|target| target.daemon_id == "yggdrasil-bifrost-persona-feedback") + .expect("Bifrost Persona-feedback target"); + assert_eq!( + target.health_contract.id, + "bifrost.cultnet-rudp-persona-feedback-health" + ); + assert_eq!( + target.deploy_command.as_deref(), + Some("E:/Projects/Odin\\scripts\\deploy-yggdrasil-bifrost-persona-feedback.ps1") + ); + assert!(target.restart_command.is_none()); + + let desired = IdunnDesiredDaemonRecord { + daemon_id: target.daemon_id.clone(), + verse_id: target.verse_id.clone(), + name: target.name.clone(), + enabled: true, + health_command: None, + restart_command: None, + deploy_command: target.deploy_command.clone(), + health_contract: target.health_contract.id.clone(), + transport_profile_id: transport_profile_id(target), + command_boundary_id: command_boundary_id(target), + authority: "idunn-supervisor-command".to_string(), + max_silence_seconds: 60, + observed_at: "unix:100".to_string(), + }; + let health = missing_daemon_published_health(target, &desired, "unix:101"); + assert_eq!(health.state, "stale-deployment"); + let plan = plan_keepalive(&desired, &health, "unix:102"); + assert_eq!(plan.decision.action, "deploy"); + assert_eq!( + plan.deployment_request.expect("deployment request").command, + target.deploy_command.clone().expect("deploy command") + ); + assert!(plan.restart_request.is_none()); + } + #[test] fn fresh_daemon_published_rudp_health_is_the_health_owner() { let desired = IdunnDesiredDaemonRecord { diff --git a/scripts/deploy-yggdrasil-bifrost-persona-feedback.ps1 b/scripts/deploy-yggdrasil-bifrost-persona-feedback.ps1 new file mode 100644 index 0000000..6a2e5e0 --- /dev/null +++ b/scripts/deploy-yggdrasil-bifrost-persona-feedback.ps1 @@ -0,0 +1,14 @@ +[CmdletBinding()] +param([string] $SshTarget = "gamecultadmin@yggdrasil.gamecult.org") +$ErrorActionPreference = "Stop" +if ($env:IDUNN_ACTUATOR -ne "1" -or $env:IDUNN_COMMAND_AUTHORITY -ne "idunn-daemon") { + throw "This command is owned by Idunn's deployment actuator." +} +if ($env:IDUNN_DEPLOYMENT_REQUEST_ID -notmatch '^deploy:yggdrasil-bifrost-persona-feedback:.+') { + throw "Idunn deployment request identity is absent or targets another daemon." +} +$requestId = $env:IDUNN_DEPLOYMENT_REQUEST_ID +if ($requestId -notmatch '^[A-Za-z0-9._:-]+$') { throw "Idunn deployment request identity contains unsafe characters." } +$remote = "sudo -n env IDUNN_ACTUATOR=1 IDUNN_COMMAND_AUTHORITY=idunn-daemon IDUNN_DEPLOYMENT_REQUEST_ID='$requestId' /usr/local/sbin/idunn-deploy-bifrost-persona-feedback-yggdrasil" +& ssh.exe $SshTarget $remote +if ($LASTEXITCODE -ne 0) { throw "Yggdrasil Bifrost Persona-feedback deployment failed with exit code $LASTEXITCODE." }