From d829c69ee26c230db9962990019be50879ed06bb Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 4 Jun 2026 21:43:41 +0100 Subject: [PATCH] Normalize legacy moderation status output --- scripts/run-void-moderator-rumination.ps1 | 53 ++++++++++++++++++++++- scripts/smoke-void-rumination-fixture.ps1 | 45 ++++++++++++++++++- state/evidence.jsonl | 1 + 3 files changed, 96 insertions(+), 3 deletions(-) diff --git a/scripts/run-void-moderator-rumination.ps1 b/scripts/run-void-moderator-rumination.ps1 index 41fa6a9..f994ceb 100644 --- a/scripts/run-void-moderator-rumination.ps1 +++ b/scripts/run-void-moderator-rumination.ps1 @@ -384,6 +384,57 @@ function Convert-ToOperationArray { return @($Value) } +function Convert-ModerationUserStatusesPatchToOperations { + param($Operation) + + $userStatuses = Get-ObjectPropertyValue -Value $Operation -Name "userStatuses" + if ($null -eq $userStatuses) { + $userStatuses = Get-ObjectPropertyValue -Value $Operation -Name "moderationUserStatuses" + } + if ($null -eq $userStatuses) { + return @() + } + + return @( + @(Convert-ToValueArray -Value $userStatuses) | + Where-Object { $null -ne $_ } | + ForEach-Object { + @{ + operation = "upsert_moderation_user_status" + status = $_ + } + } + ) +} + +function Normalize-ProposedRuminationOperations { + param($Operations) + + $normalized = @() + foreach ($operation in @(Convert-ToOperationArray -Value $Operations)) { + if ($null -eq $operation) { + continue + } + + $operationName = Get-ObjectPropertyString -Value $operation -Name "operation" + if ($null -ne $operationName) { + $normalized += $operation + continue + } + + $statusOperations = @(Convert-ModerationUserStatusesPatchToOperations -Operation $operation) + if ($statusOperations.Count -gt 0) { + Append-RunLog ("normalized legacy moderation user status patch into {0} operation(s)." -f $statusOperations.Count) + $normalized += $statusOperations + continue + } + + $normalized += $operation + } + + return @($normalized) +} + function Test-OpenCaseRequiresRumination { param($Case) @@ -1346,7 +1397,7 @@ if (-not (Test-Path $operationOutputPath)) { } Append-RunLog "reading proposed operation output." -$proposedOperations = @(Convert-ToOperationArray -Value (Read-JsonFile -Path $operationOutputPath)) +$proposedOperations = @(Normalize-ProposedRuminationOperations -Operations (Read-JsonFile -Path $operationOutputPath)) $appliedOperations = @() Assert-SpeechPressureObligationsResolved -Obligations $speechPressureObligations -Operations $proposedOperations diff --git a/scripts/smoke-void-rumination-fixture.ps1 b/scripts/smoke-void-rumination-fixture.ps1 index a137e91..df204df 100644 --- a/scripts/smoke-void-rumination-fixture.ps1 +++ b/scripts/smoke-void-rumination-fixture.ps1 @@ -157,6 +157,41 @@ process.stdin.on("end", () => { tags: ["fixture"], }, }, + { + userStatuses: [ + { + userId: "fixture-status-user", + userName: "Fixture Status User", + status: "active", + summary: "Fixture legacy patch should normalize into an upsert operation.", + strikes: [ + { + strikeId: "fixture-status-strike", + reason: "testing legacy userStatuses normalization", + ruleRef: "fixture-rule", + sourceMessageId: "fixture-status-message", + channelId: "fixture-channel", + issuedAt: "2026-06-04T00:00:00.000Z", + expiresAt: "2026-06-11T00:00:00.000Z", + issuedBy: "Void", + tags: ["fixture"], + }, + ], + pendingNotices: [ + { + noticeId: "fixture-status-notice", + kind: "strike_added", + summary: "Fixture status notice", + body: "Fixture moderation status notice.", + createdAt: "2026-06-04T00:00:00.000Z", + tags: ["fixture"], + }, + ], + updatedAt: "2026-06-04T00:00:00.000Z", + tags: ["fixture"], + }, + ], + }, ]; mkdirSync(dirname(operationOutputPath), { recursive: true }); @@ -212,8 +247,8 @@ process.stdin.on("end", () => { if ($status.status -ne "ok") { throw "Rumination fixture did not finish ok." } - if ([int]$status.proposedOperationCount -ne 4 -or [int]$status.appliedOperationCount -ne 4) { - throw "Rumination fixture expected four proposed/applied operations." + if ([int]$status.proposedOperationCount -ne 5 -or [int]$status.appliedOperationCount -ne 5) { + throw "Rumination fixture expected five proposed/applied operations." } $contextRaw = Get-Content -LiteralPath $contextPath -Raw -Encoding UTF8 @@ -241,6 +276,7 @@ process.stdin.on("end", () => { $incubation = @($state.thoughtMemory.incubation) $agency = @($state.agencyPressure.pressures) $candidates = @($state.candidateInterventions.interventions) + $moderationStatuses = @($state.moderationCursor.userStatuses) if ($shortTerm.Count -ne 1 -or $shortTerm[0].memoryId -ne "fixture-rumination-short-term") { throw "Rumination fixture did not persist the short-term memory proposal." @@ -257,6 +293,10 @@ process.stdin.on("end", () => { if ($candidates.Count -ne 1 -or $candidates[0].interventionId -ne "fixture-rumination-candidate") { throw "Rumination fixture did not persist the candidate intervention proposal." } + $userStatus = @($moderationStatuses | Where-Object { $_.userId -eq "fixture-status-user" }) | Select-Object -First 1 + if ($null -eq $userStatus -or $userStatus.strikes[0].strikeId -ne "fixture-status-strike") { + throw "Rumination fixture did not normalize the legacy userStatuses patch." + } @{ status = "ok" @@ -264,6 +304,7 @@ process.stdin.on("end", () => { incubationCount = $incubation.Count agencyPressureCount = $agency.Count candidateCount = $candidates.Count + moderationStatusCount = $moderationStatuses.Count } | ConvertTo-Json -Compress } finally { Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 2f0e158..1df15d0 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -342,3 +342,4 @@ {"ts":"2026-06-04T18:54:39Z","type":"implementation","status":"ok","note":"Created a canonical GameCult structured text CultCache store at .voidbot/private/gamecult-text-documents.cc. The first document is gamecult.odin_verse_poem with title, GameCult author, and half-line arrays; repo Face prompts now render the Odin poem from that typed document while Nightwing-style consumers can lower the same arrays with their own caesura/marquee formatting."} {"ts":"2026-06-04T19:02:38Z","type":"implementation","status":"ok","note":"Added gamecult.love_colossus_poem as the second canonical GameCult structured text document, based on the 2026-06-01 Love Is How The Colossus Wakes article and refreshed against Bellows' Poetic Edda register. The document uses title/author/half-line arrays and previews through texts:seed --document-id gamecult.love_colossus_poem."} {"ts":"2026-06-04T19:28:18Z","type":"implementation","status":"ok","note":"Moderation consequences now live in typed void.moderation_cursor userStatuses with one-week strike expiry and pending user-status notices; unattended Discord egress defaults through Bifrost bridge posts/DMs with direct VoidBot send helper retained only behind VOID_DISCORD_TRANSPORT=direct fallback."} +{"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."}