From f5c6981522feccec606edd1549bf19a3cf845f58 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 4 Jun 2026 23:29:21 +0100 Subject: [PATCH] Resolve Void persona avatar at delivery --- scripts/run-void-moderator-rumination.ps1 | 51 ++++++++++++++++++- .../smoke-void-rumination-speech-fixture.ps1 | 9 ++++ state/evidence.jsonl | 2 + 3 files changed, 61 insertions(+), 1 deletion(-) diff --git a/scripts/run-void-moderator-rumination.ps1 b/scripts/run-void-moderator-rumination.ps1 index f994ceb..e21c0f9 100644 --- a/scripts/run-void-moderator-rumination.ps1 +++ b/scripts/run-void-moderator-rumination.ps1 @@ -180,6 +180,55 @@ function Get-ConfigValue { return $null } +function Convert-ToEnvKeySuffix { + param([string] $Value) + + if ([string]::IsNullOrWhiteSpace($Value)) { + return "" + } + + return (($Value.Trim().ToUpperInvariant() -replace '[^A-Z0-9]+', '_') -replace '^_+|_+$', '') +} + +function Resolve-DeliveryPersonaAvatarUrl { + param( + $DeliveryTarget, + [string] $PersonaName + ) + + $explicitAvatarUrl = Get-ObjectPropertyString -Value $DeliveryTarget -Name "personaAvatarUrl" + if (-not [string]::IsNullOrWhiteSpace($explicitAvatarUrl)) { + return $explicitAvatarUrl.Trim() + } + + if (-not [string]::IsNullOrWhiteSpace($PersonaName)) { + $personaSuffix = Convert-ToEnvKeySuffix -Value $PersonaName + if (-not [string]::IsNullOrWhiteSpace($personaSuffix)) { + $personaKey = "DISCORD_PERSONA_AVATAR_URL_$personaSuffix" + $personaAvatarUrl = Get-ConfigValue -Name $personaKey -Values $script:envValues + if (-not [string]::IsNullOrWhiteSpace($personaAvatarUrl)) { + return $personaAvatarUrl.Trim() + } + } + } + + $defaultAvatarUrl = Get-ConfigValue -Name "DISCORD_PERSONA_AVATAR_URL" -Values $script:envValues + if (-not [string]::IsNullOrWhiteSpace($defaultAvatarUrl)) { + return $defaultAvatarUrl.Trim() + } + + if ( + -not [string]::IsNullOrWhiteSpace($PersonaName) -and + -not [string]::IsNullOrWhiteSpace($script:publicRoomPersonaName) -and + $PersonaName.Trim().Equals($script:publicRoomPersonaName.Trim(), [System.StringComparison]::OrdinalIgnoreCase) -and + -not [string]::IsNullOrWhiteSpace($script:publicRoomPersonaAvatarUrl) + ) { + return $script:publicRoomPersonaAvatarUrl.Trim() + } + + return $null +} + function Split-CommandArgs { param( [string] $Value @@ -833,7 +882,7 @@ function Invoke-CandidateInterventionDeliveryFromIntervention { $arguments += @("--persona-name", $personaName) } - $personaAvatarUrl = Get-ObjectPropertyString -Value $deliveryTarget -Name "personaAvatarUrl" + $personaAvatarUrl = Resolve-DeliveryPersonaAvatarUrl -DeliveryTarget $deliveryTarget -PersonaName $personaName if (-not [string]::IsNullOrWhiteSpace($personaAvatarUrl)) { $arguments += @("--persona-avatar-url", $personaAvatarUrl) } diff --git a/scripts/smoke-void-rumination-speech-fixture.ps1 b/scripts/smoke-void-rumination-speech-fixture.ps1 index 9aef6d9..e1d1391 100644 --- a/scripts/smoke-void-rumination-speech-fixture.ps1 +++ b/scripts/smoke-void-rumination-speech-fixture.ps1 @@ -140,6 +140,7 @@ const contentFileIndex = process.argv.indexOf("--content-file"); const channelIndex = process.argv.indexOf("--channel-id"); const replyIndex = process.argv.indexOf("--reply-to"); const personaIndex = process.argv.indexOf("--persona-name"); +const personaAvatarIndex = process.argv.indexOf("--persona-avatar-url"); if (contentFileIndex < 0 || channelIndex < 0) { console.error("fake sender expected --content-file and --channel-id"); @@ -150,6 +151,7 @@ const content = readFileSync(resolve(process.argv[contentFileIndex + 1]), "utf8" const channelId = process.argv[channelIndex + 1]; const replyToMessageId = replyIndex >= 0 ? process.argv[replyIndex + 1] : undefined; const personaName = personaIndex >= 0 ? process.argv[personaIndex + 1] : undefined; +const personaAvatarUrl = personaAvatarIndex >= 0 ? process.argv[personaAvatarIndex + 1] : undefined; const payload = { sentAt: "2026-05-17T03:01:00.000Z", mode: "channel", @@ -157,6 +159,7 @@ const payload = { channelId, replyToMessageId, personaName, + personaAvatarUrl, contentLength: content.trim().length, chunkCount: 1, preview: content.trim().slice(0, 280), @@ -220,6 +223,7 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) + $previousSendScript = $env:VOID_SEND_DISCORD_SCRIPT $previousDiscordTransport = $env:VOID_DISCORD_TRANSPORT $previousDisableRepoCursorAdvance = $env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE + $previousVoidAvatarUrl = $env:DISCORD_PERSONA_AVATAR_URL_VOID try { $env:CODEX_EXECUTABLE = "node" @@ -230,6 +234,7 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) + $env:VOID_SEND_DISCORD_SCRIPT = $fakeSendPath $env:VOID_DISCORD_TRANSPORT = "direct" $env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = "1" + $env:DISCORD_PERSONA_AVATAR_URL_VOID = "https://example.invalid/void-fixture-avatar.png" powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $stateFilePath | Out-Null } finally { @@ -241,6 +246,7 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) + $env:VOID_SEND_DISCORD_SCRIPT = $previousSendScript $env:VOID_DISCORD_TRANSPORT = $previousDiscordTransport $env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = $previousDisableRepoCursorAdvance + $env:DISCORD_PERSONA_AVATAR_URL_VOID = $previousVoidAvatarUrl } $status = Get-Content -LiteralPath $statusPath -Raw -Encoding UTF8 | ConvertFrom-Json @@ -277,6 +283,9 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) + if ($receipt.channelId -ne "fixture-channel-id" -or $receipt.replyToMessageId -ne "fixture-message-id") { throw "Speech fixture did not preserve the delivery receipt target." } + if ($receipt.personaName -ne "Void" -or $receipt.personaAvatarUrl -ne "https://example.invalid/void-fixture-avatar.png") { + throw "Speech fixture did not resolve the configured Void persona avatar at delivery time." + } if ($receipt.candidateInterventionId -ne "fixture-parent-owned-speech") { throw "Speech fixture did not link the delivery receipt back to the spoken candidate." } diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 7fa4258..62292a6 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -345,3 +345,5 @@ {"ts":"2026-06-04T20:43:18Z","type":"verification","status":"ok","note":"Rumination parent normalizes legacy top-level userStatuses model output into upsert_moderation_user_status operations; fixtures cover the stale shape and moderation status DM delivery."} {"ts":"2026-06-04T21:00:32Z","type":"implementation","status":"ok","note":"Legacy bare moderation cursor documents with top-level userStatuses now decode through voidModerationCursorSchema into canonical cursor shape; verified build, state-boundary fixture, rumination fixture, targeted bot/worker restart, and restored a lost Nibu summon through the heartbeat path (Discord message 1512199001309446295). Separate CultCache/CultMesh Extra-byte decode still remains if it recurs outside state-note-only jobs."} {"ts":"2026-06-04T22:20:09Z","type":"implementation","status":"ok","note":"Repo Face SAY routing now marks parent-Interpreter stale/older-side-thread routes as requiring an explicit reply_to anchor; worker refuses those SAY intents instead of defaulting to an unanchored current_room post. Verified build, agents:speech-boundary-smoke, moderation state-boundary fixture, and restarted worker PID 2488."} +{"ts":"2026-06-04T22:27:52Z","type":"implementation","status":"ok","note":"Void moderation candidate delivery now resolves persona avatar URLs at the parent delivery boundary when the model target names a persona but omits personaAvatarUrl. Speech fixture verifies Void delivery receives DISCORD_PERSONA_AVATAR_URL_VOID before direct/Bifrost transport."} +{"ts":"2026-06-04T22:29:15Z","type":"decision","status":"ok","note":"Transport doctrine updated by operator: VoidBot should expect GameCult infra and Bifrost/CultMesh transport; standalone native Discord fallback is no longer a product goal. Future cuts should remove direct VoidBot-native transport authority instead of preserving convenience for non-GameCult deployments."}