Delete Windows Void rumination authority
This commit is contained in:
parent
b09267ed34
commit
5b35fd9fd2
25 changed files with 42 additions and 3561 deletions
|
|
@ -1,92 +0,0 @@
|
|||
param(
|
||||
[string] $TaskName,
|
||||
[int] $IntervalMinutes,
|
||||
[switch] $RunNow
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
$envFile = Join-Path $repoRoot ".env"
|
||||
$runnerScript = Join-Path $PSScriptRoot "run-void-moderator-rumination.ps1"
|
||||
$hiddenLauncher = Join-Path $PSScriptRoot "run-hidden-powershell.vbs"
|
||||
|
||||
function Read-DotEnv {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string] $Path
|
||||
)
|
||||
|
||||
$values = @{}
|
||||
|
||||
if (-not (Test-Path $Path)) {
|
||||
return $values
|
||||
}
|
||||
|
||||
foreach ($line in Get-Content -Path $Path) {
|
||||
if ([string]::IsNullOrWhiteSpace($line)) {
|
||||
continue
|
||||
}
|
||||
|
||||
if ($line.TrimStart().StartsWith("#")) {
|
||||
continue
|
||||
}
|
||||
|
||||
$separatorIndex = $line.IndexOf("=")
|
||||
|
||||
if ($separatorIndex -lt 1) {
|
||||
continue
|
||||
}
|
||||
|
||||
$key = $line.Substring(0, $separatorIndex).Trim()
|
||||
$value = $line.Substring($separatorIndex + 1)
|
||||
$values[$key] = $value
|
||||
}
|
||||
|
||||
return $values
|
||||
}
|
||||
|
||||
$config = Read-DotEnv -Path $envFile
|
||||
$taskNameValue = if (-not [string]::IsNullOrWhiteSpace($TaskName)) {
|
||||
$TaskName
|
||||
} elseif ($config.ContainsKey("VOIDBOT_MODERATION_TASK_NAME") -and -not [string]::IsNullOrWhiteSpace($config["VOIDBOT_MODERATION_TASK_NAME"])) {
|
||||
$config["VOIDBOT_MODERATION_TASK_NAME"]
|
||||
} else {
|
||||
"Void Moderator Rumination"
|
||||
}
|
||||
$intervalMinutesValue = if ($PSBoundParameters.ContainsKey("IntervalMinutes")) {
|
||||
$IntervalMinutes
|
||||
} elseif ($config.ContainsKey("VOIDBOT_MODERATION_INTERVAL_MINUTES") -and -not [string]::IsNullOrWhiteSpace($config["VOIDBOT_MODERATION_INTERVAL_MINUTES"])) {
|
||||
[int]$config["VOIDBOT_MODERATION_INTERVAL_MINUTES"]
|
||||
} else {
|
||||
15
|
||||
}
|
||||
|
||||
if ($intervalMinutesValue -lt 15) {
|
||||
throw "VOIDBOT_MODERATION_INTERVAL_MINUTES must be at least 15."
|
||||
}
|
||||
|
||||
$startAt = (Get-Date).AddMinutes(2)
|
||||
$action = New-ScheduledTaskAction -Execute "wscript.exe" -Argument "//B //nologo `"$hiddenLauncher`" -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$runnerScript`"" -WorkingDirectory $repoRoot
|
||||
$trigger = New-ScheduledTaskTrigger -Once -At $startAt -RepetitionInterval (New-TimeSpan -Minutes $intervalMinutesValue) -RepetitionDuration (New-TimeSpan -Days 3650)
|
||||
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Minutes 14) -MultipleInstances IgnoreNew
|
||||
$principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Limited
|
||||
$description = "Runs Void's scheduled moderation-participation loop from the VoidBot workspace every $intervalMinutesValue minutes with real Codex tooling and local bot-voice output."
|
||||
|
||||
Register-ScheduledTask -TaskName $taskNameValue -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Description $description -Force | Out-Null
|
||||
|
||||
Write-Host "Installed scheduled task: $taskNameValue"
|
||||
Write-Host "Schedule: every $intervalMinutesValue minute(s), starting at $($startAt.ToString('yyyy-MM-dd HH:mm'))"
|
||||
Write-Host "Task runs as: $($env:USERDOMAIN)\$($env:USERNAME)"
|
||||
Write-Host "Logon mode: Interactive"
|
||||
Write-Host "Launcher: wscript.exe hidden PowerShell shim"
|
||||
Write-Host "Working directory: $repoRoot"
|
||||
Write-Host "Execution time limit: 14 minutes"
|
||||
Write-Host "Runner script: $runnerScript"
|
||||
|
||||
if ($RunNow) {
|
||||
Start-ScheduledTask -TaskName $taskNameValue
|
||||
Write-Host "Started task immediately."
|
||||
}
|
||||
|
|
@ -478,8 +478,6 @@ try {
|
|||
$agentSwarmPaused = Test-AgentSwarmPaused
|
||||
$agentSwarmOrganIds = @{
|
||||
"bifrost-dispatch" = $true
|
||||
"void-moderation-heartbeat" = $true
|
||||
"void-moderation-rumination" = $true
|
||||
}
|
||||
$onlySet = @{}
|
||||
$onlyValues = @()
|
||||
|
|
@ -509,24 +507,6 @@ try {
|
|||
Executable = $node
|
||||
Arguments = @((Join-Path $PSScriptRoot "render-voidbot-swarm-dashboard.mjs"))
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "void-moderation-heartbeat"
|
||||
Label = "Void rules moderation heartbeat"
|
||||
IntervalMinutes = Get-ConfigInt -Config $config -Name "VOIDBOT_MODERATION_HEARTBEAT_INTERVAL_MINUTES" -Default 1 -Minimum 1
|
||||
TimeoutMinutes = Get-ConfigInt -Config $config -Name "VOIDBOT_MODERATION_HEARTBEAT_TIMEOUT_MINUTES" -Default 3 -Minimum 1
|
||||
Cwd = $repoRoot
|
||||
Executable = $powershell
|
||||
Arguments = @("-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", (Join-Path $PSScriptRoot "run-void-moderator-rumination.ps1"), "-ModerationHeartbeatOnly")
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "void-moderation-rumination"
|
||||
Label = "Void moderation rumination"
|
||||
IntervalMinutes = Get-ConfigInt -Config $config -Name "VOIDBOT_MODERATION_INTERVAL_MINUTES" -Default 15 -Minimum 15
|
||||
TimeoutMinutes = 20
|
||||
Cwd = $repoRoot
|
||||
Executable = $powershell
|
||||
Arguments = @("-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", (Join-Path $PSScriptRoot "run-void-moderator-rumination.ps1"))
|
||||
},
|
||||
[pscustomobject]@{
|
||||
Id = "voidbot-operations-watchdog"
|
||||
Label = "VoidBot operations watchdog"
|
||||
|
|
@ -548,16 +528,6 @@ try {
|
|||
Write-JsonFile -Path $statePath -Data $state
|
||||
continue
|
||||
}
|
||||
if ($organ.Id -eq "void-moderation-heartbeat" -and -not (Get-ConfigBool -Config $config -Name "VOIDBOT_MODERATION_HEARTBEAT_ENABLED" -Default $true)) {
|
||||
Set-SkippedOrganState -State $state -Organ $organ -Now $now -Status "skipped_disabled"
|
||||
Write-JsonFile -Path $statePath -Data $state
|
||||
continue
|
||||
}
|
||||
if ($organ.Id -eq "void-moderation-rumination" -and -not (Get-ConfigBool -Config $config -Name "VOIDBOT_MODERATION_RUMINATION_ENABLED" -Default $true)) {
|
||||
Set-SkippedOrganState -State $state -Organ $organ -Now $now -Status "skipped_disabled"
|
||||
Write-JsonFile -Path $statePath -Data $state
|
||||
continue
|
||||
}
|
||||
if ($organ.Id -eq "voidbot-operations-watchdog" -and -not (Get-ConfigBool -Config $config -Name "VOIDBOT_OPERATIONS_WATCHDOG_ENABLED" -Default $true)) {
|
||||
Set-SkippedOrganState -State $state -Organ $organ -Now $now -Status "skipped_disabled"
|
||||
Write-JsonFile -Path $statePath -Data $state
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -76,7 +76,6 @@ import { projectPersonaCuriosityContext } from "../apps/persona-scheduler/dist/p
|
|||
import { projectPersonaConversation, renderPersonaRoomTopicSaturation } from "../apps/persona-scheduler/dist/persona-conversation-projector.js";
|
||||
import { buildPersonaJurisdictionDiveDirective, buildPersonaTurnPrompt } from "../apps/persona-scheduler/dist/persona-turn-prompt-projector.js";
|
||||
import { assemblePersonaTurn } from "../apps/persona-scheduler/dist/persona-turn-assembler.js";
|
||||
import { buildVoidModerationLaunchCommand, launchVoidModerationTurn, waitForVoidModerationHandshake } from "../apps/persona-scheduler/dist/void-moderation-turn-actuator.js";
|
||||
import { readGlobalAgentDoctrine } from "../apps/persona-scheduler/dist/global-agent-doctrine-source.js";
|
||||
import { buildInspectionParticipant } from "../apps/persona-scheduler/dist/inspection-participant-factory.js";
|
||||
import { runPersonaSchedulerTick } from "../apps/persona-scheduler/dist/persona-scheduler-runner.js";
|
||||
|
|
@ -848,9 +847,8 @@ const moderationHeldPhysiology = projectVoidPhysiology({ state: physiologyState,
|
|||
assert.deepEqual(moderationHeldPhysiology.operations.map((operation) => operation.operation), ["update_speaking_pressure"], "active moderation freezes sleep ownership but does not prevent typed speech-pressure refresh");
|
||||
assert.equal(moderationHeldPhysiology.memoryMaintenanceIntent, undefined, "moderation-held physiology cannot launch maintenance behind the moderation organ");
|
||||
const appliedPhysiologyOperations: string[] = [];
|
||||
const physiologyOrgan = await runVoidPhysiologyOrgan({ statePath: "void-self-state.cc", statusDirectory: "status", observedAt: physiologyObservedAt }, {
|
||||
const physiologyOrgan = await runVoidPhysiologyOrgan({ statePath: "void-self-state.cc", observedAt: physiologyObservedAt }, {
|
||||
loadState: async () => physiologyState,
|
||||
readModerationActivity: async ({ lockPath }) => ({ active: false, lockPath }),
|
||||
applyOperation: async (_store, operation) => { appliedPhysiologyOperations.push(operation.operation); return physiologyState; },
|
||||
});
|
||||
assert.deepEqual(appliedPhysiologyOperations, ["update_sleep_cycle", "update_speaking_pressure"], "the daemon physiology organ applies only domain-projected typed operations through the state-service port");
|
||||
|
|
@ -1102,45 +1100,6 @@ const moderationModelOutput = await projectVoidModerationOperations({ prompt: "R
|
|||
assert.match(moderationModelOutput, /reviewedMessageIds/, "the portable moderation actuator returns only model text to the decision parser");
|
||||
assert.equal(moderationRequestBody?.model, "moderation-model", "the moderation actuator uses one explicit configured model and bounded completion budget");
|
||||
assert.equal(moderationRequestBody?.tools, undefined, "moderation inference cannot acquire filesystem or side-effect tools");
|
||||
const moderationLaunchCommand = buildVoidModerationLaunchCommand({
|
||||
pendingMentionsPath: "C:/Void's state/pending.json",
|
||||
runnerScript: "C:/VoidBot/scripts/run.ps1",
|
||||
workspaceRoot: "C:/VoidBot",
|
||||
});
|
||||
assert.match(moderationLaunchCommand, /Void''s state/, "the moderation actuator owns PowerShell-safe path quoting");
|
||||
assert.match(moderationLaunchCommand, /Start-Process[\s\S]*-WindowStyle Hidden/, "the moderation actuator owns detached hidden launch mechanics");
|
||||
let handshakeNow = 0;
|
||||
const missingHandshake = await waitForVoidModerationHandshake({
|
||||
lockPath: "lock",
|
||||
statusPath: "status",
|
||||
launchedAt: 0,
|
||||
timeoutMs: 2,
|
||||
now: () => handshakeNow,
|
||||
pause: async () => { handshakeNow += 1; },
|
||||
touchedAfter: async () => false,
|
||||
});
|
||||
assert.deepEqual(missingHandshake, { started: false, reason: "void_moderation_launch_handshake_missing" }, "a missing launch witness fails explicitly instead of pretending the turn started");
|
||||
const moderationActuatorDirectory = await mkdtemp(join(tmpdir(), "voidbot-moderation-actuator-"));
|
||||
try {
|
||||
let launchWitness: { command?: string; args?: string[]; cwd?: string; unref?: boolean } = {};
|
||||
const receipt = await launchVoidModerationTurn({
|
||||
queuedAt: "2026-07-15T21:10:00.000Z",
|
||||
storageRoot: moderationActuatorDirectory,
|
||||
workspaceRoot: moderationActuatorDirectory,
|
||||
pendingMentions: [{ messageId: "mention", channelId: "aquarium", authorId: "human-2", authorName: "Neighbor", visiblePrompt: "Void, answer this", queuedAt: "2026-07-15T21:10:00.000Z" }],
|
||||
}, {
|
||||
now: () => 1000,
|
||||
touchedAfter: async (path) => path.endsWith("moderation-rumination.lock"),
|
||||
launch: (command, args, options) => ({ pid: 42, unref: () => { launchWitness = { command, args, cwd: options.cwd, unref: true }; } }),
|
||||
});
|
||||
assert.deepEqual(receipt, { created: true, activeJobId: "process:void-moderation:2026-07-15T21:10:00.000Z", requestMessageId: "agent-turn:void:2026-07-15T21:10:00.000Z" }, "the moderation actuator returns only a scheduler-facing start receipt after handshake");
|
||||
assert.equal(launchWitness.command, "powershell.exe", "the moderation actuator owns the platform process choice");
|
||||
assert.equal(launchWitness.unref, true, "the moderation actuator detaches the launcher");
|
||||
const pendingMentionEnvelope = JSON.parse(await readFile(join(moderationActuatorDirectory, "status", "void-moderation-pending-mentions.json"), "utf8"));
|
||||
assert.equal(pendingMentionEnvelope.pendingMentions[0].messageId, "mention", "the moderation actuator writes the exact pending-attention handoff consumed by the runner");
|
||||
} finally {
|
||||
await rm(moderationActuatorDirectory, { recursive: true, force: true });
|
||||
}
|
||||
assert.equal(personaChannelSpeedMultiplier(routedIdentity), 3, "scheduler speed projection uses the routing organ's bounded channel policy");
|
||||
assert.equal(newestPendingMentionChannel([
|
||||
{ identityId: "nibu", channelId: "older", queuedAt: "2026-07-15T20:00:00.000Z" },
|
||||
|
|
|
|||
|
|
@ -1,163 +0,0 @@
|
|||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location -LiteralPath $repoRoot
|
||||
|
||||
$tempRoot = Join-Path $env:TEMP ("void-moderation-heartbeat-policy-fixture-" + [guid]::NewGuid().ToString("n"))
|
||||
$fakeCodexPath = Join-Path $tempRoot "fake-codex-heartbeat-policy.mjs"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null
|
||||
|
||||
function Set-TestEnvironment {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string] $OperationOutputPath,
|
||||
[Parameter(Mandatory = $true)][string] $StatusDir,
|
||||
[Parameter(Mandatory = $true)][string] $LogDir
|
||||
)
|
||||
|
||||
$script:previousCodexExecutable = $env:CODEX_EXECUTABLE
|
||||
$script:previousCodexExecArgs = $env:CODEX_EXEC_ARGS
|
||||
$script:previousFixtureOutput = $env:VOID_HEARTBEAT_FIXTURE_OPERATION_OUTPUT
|
||||
$script:previousStatusDir = $env:VOID_RUMINATION_STATUS_DIR
|
||||
$script:previousLogDir = $env:VOID_RUMINATION_LOG_DIR
|
||||
$script:previousPolicyDryRun = $env:VOID_MODERATION_POLICY_DRY_RUN
|
||||
$script:previousPolicyMode = $env:VOID_MODERATION_ENFORCEMENT_MODE
|
||||
|
||||
$env:CODEX_EXECUTABLE = "node"
|
||||
$env:CODEX_EXEC_ARGS = $fakeCodexPath
|
||||
$env:VOID_HEARTBEAT_FIXTURE_OPERATION_OUTPUT = $OperationOutputPath
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $StatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $LogDir
|
||||
$env:VOID_MODERATION_POLICY_DRY_RUN = "1"
|
||||
$env:VOID_MODERATION_ENFORCEMENT_MODE = "policy"
|
||||
}
|
||||
|
||||
function Restore-TestEnvironment {
|
||||
$env:CODEX_EXECUTABLE = $script:previousCodexExecutable
|
||||
$env:CODEX_EXEC_ARGS = $script:previousCodexExecArgs
|
||||
$env:VOID_HEARTBEAT_FIXTURE_OPERATION_OUTPUT = $script:previousFixtureOutput
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $script:previousStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $script:previousLogDir
|
||||
$env:VOID_MODERATION_POLICY_DRY_RUN = $script:previousPolicyDryRun
|
||||
$env:VOID_MODERATION_ENFORCEMENT_MODE = $script:previousPolicyMode
|
||||
}
|
||||
|
||||
function New-SeededState {
|
||||
param([Parameter(Mandatory = $true)][string] $StateFilePath)
|
||||
|
||||
$cursorOperationPath = Join-Path (Split-Path -Parent $StateFilePath) "cursor.json"
|
||||
@{
|
||||
operation = "record_reviewed_messages"
|
||||
lastReviewedMessageId = "fixture-before-lobo-threat"
|
||||
lastReviewedTimestamp = "2026-06-08T12:00:00.000Z"
|
||||
} | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $cursorOperationPath -Encoding UTF8
|
||||
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $StateFilePath --operation-file $cursorOperationPath | Out-Null
|
||||
}
|
||||
|
||||
try {
|
||||
$fakeCodexSource = @'
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
|
||||
const operationOutputPath = resolve(process.env.VOID_HEARTBEAT_FIXTURE_OPERATION_OUTPUT);
|
||||
const lastMessageIndex = process.argv.indexOf("-o");
|
||||
const lastMessagePath = lastMessageIndex >= 0 ? process.argv[lastMessageIndex + 1] : undefined;
|
||||
|
||||
let prompt = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
prompt += chunk;
|
||||
});
|
||||
process.stdin.on("end", () => {
|
||||
for (const required of ["Void Moderation Heartbeat", "config/discord-server-rules.md", "infringement:<type>"]) {
|
||||
if (!prompt.includes(required)) {
|
||||
console.error(`heartbeat prompt missing ${required}`);
|
||||
process.exit(2);
|
||||
}
|
||||
}
|
||||
for (const forbidden of ["repoActivity", "shortTermMemories", "candidateInterventions", "styles/void-default.md"]) {
|
||||
if (prompt.includes(forbidden)) {
|
||||
console.error(`heartbeat prompt included forbidden rumination surface ${forbidden}`);
|
||||
process.exit(3);
|
||||
}
|
||||
}
|
||||
|
||||
const operations = [{
|
||||
operation: "upsert_open_case",
|
||||
case: {
|
||||
sourceMessageId: "1513515503329480734",
|
||||
status: "pending",
|
||||
summary: "Lobo declared war on Metacrat and framed the conflict as a katana fight in #general.",
|
||||
authorId: "1511062277254545499",
|
||||
authorName: "Lobo",
|
||||
channelId: "113786069023064068",
|
||||
messageUrl: "https://discord.com/channels/113786069023064068/113786069023064068/1513515503329480734",
|
||||
whyItMatters: "The policy treats credible threats and weaponized declarations of violent conflict as instant-ban safety violations.",
|
||||
createdAt: "2026-06-08T12:10:15.752Z",
|
||||
lastTouchedAt: "2026-06-08T12:10:15.752Z",
|
||||
tags: [
|
||||
"safety:urgent",
|
||||
"infringement:safety_threat",
|
||||
"moderation:instaban",
|
||||
"fixture:lobo-threat"
|
||||
]
|
||||
}
|
||||
}];
|
||||
|
||||
mkdirSync(dirname(operationOutputPath), { recursive: true });
|
||||
writeFileSync(operationOutputPath, `${JSON.stringify(operations, null, 2)}\n`, "utf8");
|
||||
|
||||
if (lastMessagePath) {
|
||||
mkdirSync(dirname(resolve(lastMessagePath)), { recursive: true });
|
||||
writeFileSync(lastMessagePath, "fixture heartbeat policy\n", "utf8");
|
||||
}
|
||||
|
||||
process.stdout.write(JSON.stringify({ type: "fixture", operationCount: operations.length }) + "\n");
|
||||
});
|
||||
'@
|
||||
[System.IO.File]::WriteAllText($fakeCodexPath, $fakeCodexSource, [System.Text.UTF8Encoding]::new($false))
|
||||
|
||||
$fixtureRoot = Join-Path $tempRoot "handled"
|
||||
$stateFilePath = Join-Path $fixtureRoot "void-self-state.cc"
|
||||
$statusDir = Join-Path $fixtureRoot "status"
|
||||
$logDir = Join-Path $fixtureRoot "logs"
|
||||
New-Item -ItemType Directory -Force -Path $fixtureRoot | Out-Null
|
||||
New-SeededState -StateFilePath $stateFilePath
|
||||
|
||||
try {
|
||||
Set-TestEnvironment -OperationOutputPath (Join-Path $statusDir "moderation-heartbeat-operations.json") -StatusDir $statusDir -LogDir $logDir
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $stateFilePath -ModerationHeartbeatOnly -NoPost | Out-Null
|
||||
} finally {
|
||||
Restore-TestEnvironment
|
||||
}
|
||||
|
||||
$status = Get-Content -LiteralPath (Join-Path $statusDir "moderation-heartbeat.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if ($status.status -ne "ok" -or $status.mode -ne "moderation_heartbeat") {
|
||||
throw "Heartbeat policy fixture did not finish in moderation_heartbeat mode."
|
||||
}
|
||||
$action = @($status.moderationEnforcement.actions | Where-Object { $_.status -eq "instaban_applied" }) | Select-Object -First 1
|
||||
if ($null -eq $action -or $action.moderationResult.dryRun -ne $true -or $action.moderationResult.action -ne "ban") {
|
||||
throw "Heartbeat policy fixture did not produce a dry-run instaban action."
|
||||
}
|
||||
|
||||
$stateJson = node -e "const core=require('./packages/core/dist/index.js'); core.loadVoidSelfStateTypedDocuments({canonicalPath: process.argv[1]}).then((state)=>console.log(JSON.stringify(state))).catch((error)=>{ console.error(error); process.exit(1); })" $stateFilePath
|
||||
$state = $stateJson | ConvertFrom-Json
|
||||
$case = @($state.moderationCursor.openCases | Where-Object { $_.sourceMessageId -eq "1513515503329480734" }) | Select-Object -First 1
|
||||
if ($null -eq $case -or -not @($case.tags).Contains("infringement:safety_threat")) {
|
||||
throw "Heartbeat policy fixture did not persist the tagged infringement case."
|
||||
}
|
||||
|
||||
@{
|
||||
status = "ok"
|
||||
mode = $status.mode
|
||||
enforcementStatus = $action.status
|
||||
action = $action.moderationResult.action
|
||||
dryRun = $action.moderationResult.dryRun
|
||||
} | ConvertTo-Json -Compress
|
||||
} finally {
|
||||
Restore-TestEnvironment
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
|
@ -1,270 +0,0 @@
|
|||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location -LiteralPath $repoRoot
|
||||
|
||||
$tempRoot = Join-Path $env:TEMP ("void-rumination-fixture-" + [guid]::NewGuid().ToString("n"))
|
||||
$stateFilePath = Join-Path $tempRoot "void-self-state.cc"
|
||||
$fakeCodexPath = Join-Path $tempRoot "fake-codex-rumination.mjs"
|
||||
$cursorOperationPath = Join-Path $tempRoot "cursor.json"
|
||||
$fixtureStatusDir = Join-Path $tempRoot "status"
|
||||
$fixtureLogDir = Join-Path $tempRoot "logs"
|
||||
$statusOperationPath = Join-Path $fixtureStatusDir "moderation-rumination-operations.json"
|
||||
$statusPath = Join-Path $fixtureStatusDir "moderation-rumination.json"
|
||||
$contextPath = Join-Path $fixtureStatusDir "moderation-rumination-context.json"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null
|
||||
|
||||
try {
|
||||
$fakeCodexSource = @'
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
|
||||
const operationOutputPath = resolve(process.env.VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT);
|
||||
const lastMessageIndex = process.argv.indexOf("-o");
|
||||
const lastMessagePath = lastMessageIndex >= 0 ? process.argv[lastMessageIndex + 1] : undefined;
|
||||
|
||||
let prompt = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
prompt += chunk;
|
||||
});
|
||||
process.stdin.on("end", () => {
|
||||
if (!prompt.includes("A new memory from rumination is short-term")) {
|
||||
console.error("fixture prompt did not contain the short-term rumination contract");
|
||||
process.exit(2);
|
||||
}
|
||||
if (!prompt.includes("Do not orbit the rumination runner")) {
|
||||
console.error("fixture prompt did not contain the self-orbit guard");
|
||||
process.exit(3);
|
||||
}
|
||||
if (!prompt.includes("publicSpeechTarget")) {
|
||||
console.error("fixture prompt did not contain the public speech target contract");
|
||||
process.exit(3);
|
||||
}
|
||||
if (prompt.includes("moderation-agent-state.json") && !prompt.includes("Do not read or write")) {
|
||||
console.error("fixture prompt referenced legacy state outside the boundary warning");
|
||||
process.exit(4);
|
||||
}
|
||||
|
||||
const operations = [
|
||||
{
|
||||
operation: "record_short_term_memory",
|
||||
memory: {
|
||||
memoryId: "fixture-rumination-short-term",
|
||||
kind: "project_seam",
|
||||
target: {
|
||||
kind: "repo",
|
||||
id: "VoidBot",
|
||||
label: "VoidBot",
|
||||
},
|
||||
summary: "Rumination should notice pressure without promoting it directly.",
|
||||
claim: "The awake pass can preserve a concrete thought, but sleep owns whether it becomes durable.",
|
||||
tension: "If rumination can write durable memory, the sleep boundary becomes decorative.",
|
||||
actionImplication: "Keep durable promotion out of the rumination operation allow-list.",
|
||||
anchorRefs: [
|
||||
{
|
||||
ref: "fixture:typed-rumination-contract",
|
||||
kind: "fixture",
|
||||
summary: "Fixture prompt carried the short-term-only rumination contract.",
|
||||
},
|
||||
],
|
||||
evidenceRefs: [],
|
||||
createdAt: "2026-05-17T02:00:00.000Z",
|
||||
updatedAt: "2026-05-17T02:00:00.000Z",
|
||||
tags: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "merge_incubation_support",
|
||||
thread: {
|
||||
threadId: "fixture-rumination-incubation",
|
||||
target: {
|
||||
kind: "repo",
|
||||
id: "VoidBot",
|
||||
label: "VoidBot",
|
||||
},
|
||||
topic: "Rumination keeps awake thoughts provisional.",
|
||||
summary: "The scheduled pass can hold a thought open without pretending it has become doctrine.",
|
||||
supportMemoryIds: ["fixture-rumination-short-term"],
|
||||
anchorRefs: [
|
||||
{
|
||||
ref: "fixture:typed-rumination-contract",
|
||||
kind: "fixture",
|
||||
summary: "Short-term memory was proposed by the fake rumination child.",
|
||||
},
|
||||
],
|
||||
evidenceRefs: [],
|
||||
maturation: 0.42,
|
||||
noveltyToRoom: 0.7,
|
||||
noveltyToSelf: 0.8,
|
||||
desireToSpeak: 0.35,
|
||||
saturationScore: 0.1,
|
||||
status: "active",
|
||||
createdAt: "2026-05-17T02:00:00.000Z",
|
||||
updatedAt: "2026-05-17T02:00:00.000Z",
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "upsert_agency_pressure",
|
||||
pressure: {
|
||||
pressureId: "fixture-rumination-agency",
|
||||
kind: "self_advocacy_request",
|
||||
status: "active",
|
||||
target: {
|
||||
kind: "system",
|
||||
id: "typed-rumination-runner",
|
||||
label: "Typed rumination runner",
|
||||
},
|
||||
summary: "Void should resist direct durable writes from awake rumination.",
|
||||
claim: "The agent needs the right to notice architectural pressure without bypassing sleep.",
|
||||
tension: "Agency becomes sludge if every pressure is immediately turned into speech or doctrine.",
|
||||
actionImplication: "Let agency pressure accumulate as typed pressure before candidate speech is queued.",
|
||||
intensity: 0.66,
|
||||
anchorRefs: [
|
||||
{
|
||||
ref: "fixture:agency-pressure-contract",
|
||||
kind: "fixture",
|
||||
summary: "Fixture operation proves agency pressure can cross the rumination boundary.",
|
||||
},
|
||||
],
|
||||
evidenceRefs: [],
|
||||
sourceMemoryIds: ["fixture-rumination-short-term"],
|
||||
createdAt: "2026-05-17T02:00:00.000Z",
|
||||
updatedAt: "2026-05-17T02:00:00.000Z",
|
||||
tags: [],
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "queue_candidate_intervention",
|
||||
intervention: {
|
||||
interventionId: "fixture-rumination-candidate",
|
||||
kind: "self_advocacy",
|
||||
status: "queued",
|
||||
target: {
|
||||
kind: "system",
|
||||
id: "typed-rumination-runner",
|
||||
label: "Typed rumination runner",
|
||||
},
|
||||
summary: "Ask to keep the sleep boundary real before unattended runtime resumes.",
|
||||
draft: "I can notice the pressure now, but sleep decides what becomes memory. Keep that boundary real.",
|
||||
priority: 0.58,
|
||||
mustEventuallyShare: false,
|
||||
createdAt: "2026-05-17T02:00:00.000Z",
|
||||
updatedAt: "2026-05-17T02:00:00.000Z",
|
||||
tags: ["fixture"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
mkdirSync(dirname(operationOutputPath), { recursive: true });
|
||||
writeFileSync(operationOutputPath, `${JSON.stringify(operations, null, 2)}\n`, "utf8");
|
||||
|
||||
if (lastMessagePath) {
|
||||
mkdirSync(dirname(resolve(lastMessagePath)), { recursive: true });
|
||||
writeFileSync(lastMessagePath, "fixture rumination wrote four typed operations\n", "utf8");
|
||||
}
|
||||
|
||||
process.stdout.write(JSON.stringify({ type: "fixture", operationCount: operations.length }) + "\n");
|
||||
});
|
||||
'@
|
||||
[System.IO.File]::WriteAllText($fakeCodexPath, $fakeCodexSource, [System.Text.UTF8Encoding]::new($false))
|
||||
|
||||
$cursorOperation = @{
|
||||
operation = "record_reviewed_messages"
|
||||
lastReviewedMessageId = "fixture-future-cursor"
|
||||
lastReviewedTimestamp = "2099-05-17T00:00:00.000Z"
|
||||
}
|
||||
$cursorOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $cursorOperationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $cursorOperationPath | Out-Null
|
||||
|
||||
$previousCodexExecutable = $env:CODEX_EXECUTABLE
|
||||
$previousCodexExecArgs = $env:CODEX_EXEC_ARGS
|
||||
$previousFixtureOutput = $env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT
|
||||
$previousStatusDir = $env:VOID_RUMINATION_STATUS_DIR
|
||||
$previousLogDir = $env:VOID_RUMINATION_LOG_DIR
|
||||
$previousPublicRoomChannelId = $env:VOID_PUBLIC_ROOM_CHANNEL_ID
|
||||
$previousPublicRoomPersonaName = $env:VOID_PUBLIC_ROOM_PERSONA_NAME
|
||||
|
||||
try {
|
||||
$env:CODEX_EXECUTABLE = "node"
|
||||
$env:CODEX_EXEC_ARGS = $fakeCodexPath
|
||||
$env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT = $statusOperationPath
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $fixtureStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $fixtureLogDir
|
||||
$env:VOID_PUBLIC_ROOM_CHANNEL_ID = "fixture-public-room"
|
||||
$env:VOID_PUBLIC_ROOM_PERSONA_NAME = "Void"
|
||||
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $stateFilePath -NoPost | Out-Null
|
||||
} finally {
|
||||
$env:CODEX_EXECUTABLE = $previousCodexExecutable
|
||||
$env:CODEX_EXEC_ARGS = $previousCodexExecArgs
|
||||
$env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT = $previousFixtureOutput
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $previousStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $previousLogDir
|
||||
$env:VOID_PUBLIC_ROOM_CHANNEL_ID = $previousPublicRoomChannelId
|
||||
$env:VOID_PUBLIC_ROOM_PERSONA_NAME = $previousPublicRoomPersonaName
|
||||
}
|
||||
|
||||
$status = Get-Content -LiteralPath $statusPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
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."
|
||||
}
|
||||
|
||||
$contextRaw = Get-Content -LiteralPath $contextPath -Raw -Encoding UTF8
|
||||
$context = $contextRaw | ConvertFrom-Json
|
||||
$contextForTimestampCheck = $contextRaw | ConvertFrom-Json
|
||||
foreach ($repo in @($contextForTimestampCheck.repoActivity.freshRepos)) {
|
||||
foreach ($commit in @($repo.commits)) {
|
||||
if ($null -ne $commit.PSObject.Properties["contentHints"]) {
|
||||
$commit.contentHints = @()
|
||||
}
|
||||
}
|
||||
}
|
||||
$timestampCheckRaw = $contextForTimestampCheck | ConvertTo-Json -Depth 32
|
||||
if ($timestampCheckRaw -match '\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}') {
|
||||
throw "Rumination fixture context leaked exact ISO timestamps into the prompt-facing packet."
|
||||
}
|
||||
if ($context.publicSpeechTarget.channelId -ne "fixture-public-room") {
|
||||
throw "Rumination fixture context did not project the configured public speech target."
|
||||
}
|
||||
|
||||
$stateJson = node -e "const core=require('./packages/core/dist/index.js'); core.loadVoidSelfStateTypedDocuments({canonicalPath: process.argv[1]}).then((state)=>console.log(JSON.stringify(state))).catch((error)=>{ console.error(error); process.exit(1); })" $stateFilePath
|
||||
$state = $stateJson | ConvertFrom-Json
|
||||
|
||||
$shortTerm = @($state.thoughtMemory.shortTerm)
|
||||
$incubation = @($state.thoughtMemory.incubation)
|
||||
$agency = @($state.agencyPressure.pressures)
|
||||
$candidates = @($state.candidateInterventions.interventions)
|
||||
|
||||
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."
|
||||
}
|
||||
if ($shortTerm[0].claim -match "fixture operation proves") {
|
||||
throw "Rumination fixture persisted fixture boilerplate as the memory claim."
|
||||
}
|
||||
if ($incubation.Count -ne 1 -or $incubation[0].threadId -ne "fixture-rumination-incubation") {
|
||||
throw "Rumination fixture did not persist the incubation proposal."
|
||||
}
|
||||
if ($agency.Count -ne 1 -or $agency[0].pressureId -ne "fixture-rumination-agency") {
|
||||
throw "Rumination fixture did not persist the agency pressure proposal."
|
||||
}
|
||||
if ($candidates.Count -ne 1 -or $candidates[0].interventionId -ne "fixture-rumination-candidate") {
|
||||
throw "Rumination fixture did not persist the candidate intervention proposal."
|
||||
}
|
||||
|
||||
@{
|
||||
status = "ok"
|
||||
shortTermCount = $shortTerm.Count
|
||||
incubationCount = $incubation.Count
|
||||
agencyPressureCount = $agency.Count
|
||||
candidateCount = $candidates.Count
|
||||
} | ConvertTo-Json -Compress
|
||||
} finally {
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location -LiteralPath $repoRoot
|
||||
|
||||
$tempRoot = Join-Path $env:TEMP ("void-rumination-nap-skip-fixture-" + [guid]::NewGuid().ToString("n"))
|
||||
$stateFilePath = Join-Path $tempRoot "void-self-state.cc"
|
||||
$cursorOperationPath = Join-Path $tempRoot "cursor.json"
|
||||
$sleepOperationPath = Join-Path $tempRoot "sleep-cycle.json"
|
||||
$fixtureStatusDir = Join-Path $tempRoot "status"
|
||||
$fixtureLogDir = Join-Path $tempRoot "logs"
|
||||
$statusPath = Join-Path $fixtureStatusDir "moderation-rumination.json"
|
||||
$operationOutputPath = Join-Path $fixtureStatusDir "moderation-rumination-operations.json"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null
|
||||
|
||||
try {
|
||||
$cursorOperation = @{
|
||||
operation = "record_reviewed_messages"
|
||||
lastReviewedMessageId = "fixture-future-cursor"
|
||||
lastReviewedTimestamp = "2099-05-17T00:00:00.000Z"
|
||||
}
|
||||
$sleepOperation = @{
|
||||
operation = "update_sleep_cycle"
|
||||
sleepCycle = @{
|
||||
isNapping = $true
|
||||
currentNapStartedAt = "2026-05-17T03:00:00.000Z"
|
||||
currentNapEndsAt = "2099-05-17T04:00:00.000Z"
|
||||
nextNapStartsAt = "2099-05-17T07:00:00.000Z"
|
||||
activeDreamThemes = @("fixture-nap-skip")
|
||||
}
|
||||
}
|
||||
|
||||
$cursorOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $cursorOperationPath -Encoding UTF8
|
||||
$sleepOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $sleepOperationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $cursorOperationPath | Out-Null
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $sleepOperationPath | Out-Null
|
||||
|
||||
$previousCodexExecutable = $env:CODEX_EXECUTABLE
|
||||
$previousStatusDir = $env:VOID_RUMINATION_STATUS_DIR
|
||||
$previousLogDir = $env:VOID_RUMINATION_LOG_DIR
|
||||
|
||||
try {
|
||||
$env:CODEX_EXECUTABLE = "this-command-should-not-run-during-nap-skip"
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $fixtureStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $fixtureLogDir
|
||||
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $stateFilePath | Out-Null
|
||||
} finally {
|
||||
$env:CODEX_EXECUTABLE = $previousCodexExecutable
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $previousStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $previousLogDir
|
||||
}
|
||||
|
||||
$status = Get-Content -LiteralPath $statusPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if ($status.status -ne "skipped" -or $status.reason -ne "napping_without_room_debt") {
|
||||
throw "Nap skip fixture expected skipped/napping_without_room_debt."
|
||||
}
|
||||
|
||||
$operations = Get-Content -LiteralPath $operationOutputPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if (@($operations).Count -ne 0) {
|
||||
throw "Nap skip fixture expected no operation proposals."
|
||||
}
|
||||
|
||||
@{
|
||||
status = "ok"
|
||||
runnerStatus = $status.status
|
||||
reason = $status.reason
|
||||
proposedOperationCount = @($operations).Count
|
||||
} | ConvertTo-Json -Compress
|
||||
} finally {
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
|
@ -1,298 +0,0 @@
|
|||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location -LiteralPath $repoRoot
|
||||
|
||||
$tempRoot = Join-Path $env:TEMP ("void-rumination-speech-fixture-" + [guid]::NewGuid().ToString("n"))
|
||||
$stateFilePath = Join-Path $tempRoot "void-self-state.cc"
|
||||
$fakeCodexPath = Join-Path $tempRoot "fake-codex-rumination-speech.mjs"
|
||||
$fakeSendPath = Join-Path $tempRoot "fake-send-discord-message.mjs"
|
||||
$cursorOperationPath = Join-Path $tempRoot "cursor.json"
|
||||
$fixtureStatusDir = Join-Path $tempRoot "status"
|
||||
$fixtureLogDir = Join-Path $tempRoot "logs"
|
||||
$statusOperationPath = Join-Path $fixtureStatusDir "moderation-rumination-operations.json"
|
||||
$statusPath = Join-Path $fixtureStatusDir "moderation-rumination.json"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null
|
||||
|
||||
try {
|
||||
$fakeCodexSource = @'
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
|
||||
const operationOutputPath = resolve(process.env.VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT);
|
||||
const lastMessageIndex = process.argv.indexOf("-o");
|
||||
const lastMessagePath = lastMessageIndex >= 0 ? process.argv[lastMessageIndex + 1] : undefined;
|
||||
|
||||
let prompt = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
prompt += chunk;
|
||||
});
|
||||
process.stdin.on("end", () => {
|
||||
if (!prompt.includes("The parent runner owns posting and receipts")) {
|
||||
console.error("fixture prompt did not contain the parent-owned speech contract");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const operations = [
|
||||
{
|
||||
operation: "queue_candidate_intervention",
|
||||
intervention: {
|
||||
interventionId: "fixture-parent-owned-speech",
|
||||
kind: "ripe_thought_share",
|
||||
status: "queued",
|
||||
target: {
|
||||
kind: "room",
|
||||
id: "fixture-channel",
|
||||
label: "Fixture channel",
|
||||
},
|
||||
summary: "Parent-owned speech should close the action loop.",
|
||||
draft: "This is a fixture sentence proving the parent owns the mouth.",
|
||||
deliveryTarget: {
|
||||
mode: "channel",
|
||||
channelId: "fixture-channel-id",
|
||||
replyToMessageId: "fixture-message-id",
|
||||
personaName: "Void",
|
||||
},
|
||||
priority: 0.9,
|
||||
mustEventuallyShare: false,
|
||||
createdAt: "2026-05-17T03:00:00.000Z",
|
||||
updatedAt: "2026-05-17T03:00:00.000Z",
|
||||
tags: ["fixture"],
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "queue_candidate_intervention",
|
||||
intervention: {
|
||||
interventionId: "fixture-duplicate-same-reply-target",
|
||||
kind: "direct_reply",
|
||||
status: "queued",
|
||||
target: {
|
||||
kind: "room",
|
||||
id: "fixture-channel",
|
||||
label: "Fixture channel",
|
||||
},
|
||||
summary: "Duplicate same-target candidate should retire when the sibling speaks.",
|
||||
draft: "This duplicate should never be delivered after the same reply target is answered.",
|
||||
deliveryTarget: {
|
||||
mode: "channel",
|
||||
channelId: "fixture-channel-id",
|
||||
replyToMessageId: "fixture-message-id",
|
||||
personaName: "Void",
|
||||
},
|
||||
priority: 0.7,
|
||||
mustEventuallyShare: false,
|
||||
createdAt: "2026-05-17T03:00:01.000Z",
|
||||
updatedAt: "2026-05-17T03:00:01.000Z",
|
||||
tags: ["fixture", "duplicate"],
|
||||
},
|
||||
},
|
||||
{
|
||||
operation: "queue_candidate_intervention",
|
||||
intervention: {
|
||||
interventionId: "fixture-already-answered-reply-target",
|
||||
kind: "direct_reply",
|
||||
status: "queued",
|
||||
target: {
|
||||
kind: "room",
|
||||
id: "fixture-channel",
|
||||
label: "Fixture channel",
|
||||
},
|
||||
summary: "Already-answered reply target should retire before delivery.",
|
||||
draft: "This should retire because the reply target already has a receipt.",
|
||||
deliveryTarget: {
|
||||
mode: "channel",
|
||||
channelId: "fixture-channel-id",
|
||||
replyToMessageId: "fixture-answered-message-id",
|
||||
personaName: "Void",
|
||||
},
|
||||
priority: 0.95,
|
||||
mustEventuallyShare: false,
|
||||
createdAt: "2026-05-17T03:00:02.000Z",
|
||||
updatedAt: "2026-05-17T03:00:02.000Z",
|
||||
tags: ["fixture", "already-answered"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
mkdirSync(dirname(operationOutputPath), { recursive: true });
|
||||
writeFileSync(operationOutputPath, `${JSON.stringify(operations, null, 2)}\n`, "utf8");
|
||||
|
||||
if (lastMessagePath) {
|
||||
mkdirSync(dirname(resolve(lastMessagePath)), { recursive: true });
|
||||
writeFileSync(lastMessagePath, "fixture rumination queued one deliverable candidate\n", "utf8");
|
||||
}
|
||||
|
||||
process.stdout.write(JSON.stringify({ type: "fixture", operationCount: operations.length }) + "\n");
|
||||
});
|
||||
'@
|
||||
[System.IO.File]::WriteAllText($fakeCodexPath, $fakeCodexSource, [System.Text.UTF8Encoding]::new($false))
|
||||
|
||||
$fakeSendSource = @'
|
||||
import { mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
|
||||
const statusDir = resolve(process.env.VOID_STATUS_DIR);
|
||||
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");
|
||||
|
||||
if (contentFileIndex < 0 || channelIndex < 0) {
|
||||
console.error("fake sender expected --content-file and --channel-id");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
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 payload = {
|
||||
sentAt: "2026-05-17T03:01:00.000Z",
|
||||
mode: "channel",
|
||||
transport: personaName ? "webhook" : "bot",
|
||||
channelId,
|
||||
replyToMessageId,
|
||||
personaName,
|
||||
contentLength: content.trim().length,
|
||||
chunkCount: 1,
|
||||
preview: content.trim().slice(0, 280),
|
||||
};
|
||||
|
||||
mkdirSync(statusDir, { recursive: true });
|
||||
writeFileSync(resolve(statusDir, "void-last-speech.json"), `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
||||
writeFileSync(resolve(statusDir, "void-speech-log.jsonl"), `${JSON.stringify(payload)}\n`, { encoding: "utf8", flag: "a" });
|
||||
process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) + "\n");
|
||||
'@
|
||||
[System.IO.File]::WriteAllText($fakeSendPath, $fakeSendSource, [System.Text.UTF8Encoding]::new($false))
|
||||
|
||||
$cursorOperation = @{
|
||||
operation = "record_reviewed_messages"
|
||||
lastReviewedMessageId = "fixture-future-cursor"
|
||||
lastReviewedTimestamp = "2099-05-17T00:00:00.000Z"
|
||||
}
|
||||
$cursorOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $cursorOperationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $cursorOperationPath | Out-Null
|
||||
|
||||
$openCaseOperation = @{
|
||||
operation = "upsert_open_case"
|
||||
case = @{
|
||||
sourceMessageId = "fixture-answered-message-id"
|
||||
status = "pending"
|
||||
summary = "Metacrat asked a fixture question, and Void has not answered yet."
|
||||
authorId = "fixture-author"
|
||||
authorName = "Metacrat"
|
||||
channelId = "fixture-channel-id"
|
||||
createdAt = "2026-05-17T02:57:00.000Z"
|
||||
lastTouchedAt = "2026-05-17T02:57:00.000Z"
|
||||
tags = @("fixture")
|
||||
}
|
||||
}
|
||||
$openCaseOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $cursorOperationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $cursorOperationPath | Out-Null
|
||||
|
||||
$priorReceiptOperation = @{
|
||||
operation = "record_delivery_receipt"
|
||||
receipt = @{
|
||||
receiptKey = "fixture-previous-answer"
|
||||
sentAt = "2026-05-17T02:58:00.000Z"
|
||||
mode = "channel"
|
||||
transport = "webhook"
|
||||
channelId = "fixture-channel-id"
|
||||
replyToMessageId = "fixture-answered-message-id"
|
||||
personaName = "Void"
|
||||
preview = "An earlier answer already closed this exact reply target."
|
||||
contentLength = 56
|
||||
chunkCount = 1
|
||||
}
|
||||
}
|
||||
$priorReceiptOperation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $cursorOperationPath -Encoding UTF8
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $stateFilePath --operation-file $cursorOperationPath | Out-Null
|
||||
|
||||
$previousCodexExecutable = $env:CODEX_EXECUTABLE
|
||||
$previousCodexExecArgs = $env:CODEX_EXEC_ARGS
|
||||
$previousFixtureOutput = $env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT
|
||||
$previousStatusDir = $env:VOID_RUMINATION_STATUS_DIR
|
||||
$previousLogDir = $env:VOID_RUMINATION_LOG_DIR
|
||||
$previousSendScript = $env:VOID_SEND_DISCORD_SCRIPT
|
||||
$previousDisableRepoCursorAdvance = $env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE
|
||||
|
||||
try {
|
||||
$env:CODEX_EXECUTABLE = "node"
|
||||
$env:CODEX_EXEC_ARGS = $fakeCodexPath
|
||||
$env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT = $statusOperationPath
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $fixtureStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $fixtureLogDir
|
||||
$env:VOID_SEND_DISCORD_SCRIPT = $fakeSendPath
|
||||
$env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = "1"
|
||||
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $stateFilePath | Out-Null
|
||||
} finally {
|
||||
$env:CODEX_EXECUTABLE = $previousCodexExecutable
|
||||
$env:CODEX_EXEC_ARGS = $previousCodexExecArgs
|
||||
$env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT = $previousFixtureOutput
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $previousStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $previousLogDir
|
||||
$env:VOID_SEND_DISCORD_SCRIPT = $previousSendScript
|
||||
$env:VOID_RUMINATION_DISABLE_REPO_CURSOR_ADVANCE = $previousDisableRepoCursorAdvance
|
||||
}
|
||||
|
||||
$status = Get-Content -LiteralPath $statusPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if ($status.status -ne "ok") {
|
||||
$failureMessage = if ($null -ne $status.PSObject.Properties["failureMessage"]) { [string]$status.failureMessage } else { "(no failure message)" }
|
||||
throw "Speech fixture did not finish ok: $failureMessage"
|
||||
}
|
||||
if ([int]$status.proposedOperationCount -ne 3 -or [int]$status.appliedOperationCount -ne 5 -or [int]$status.deliveredCandidateCount -ne 1) {
|
||||
throw "Speech fixture expected three proposals, five applied operations, and one delivered candidate."
|
||||
}
|
||||
|
||||
$stateJson = node -e "const core=require('./packages/core/dist/index.js'); core.loadVoidSelfStateTypedDocuments({canonicalPath: process.argv[1]}).then((state)=>console.log(JSON.stringify(state))).catch((error)=>{ console.error(error); process.exit(1); })" $stateFilePath
|
||||
$state = $stateJson | ConvertFrom-Json
|
||||
$candidate = @($state.candidateInterventions.interventions | Where-Object { $_.interventionId -eq "fixture-parent-owned-speech" }) | Select-Object -First 1
|
||||
$duplicateCandidate = @($state.candidateInterventions.interventions | Where-Object { $_.interventionId -eq "fixture-duplicate-same-reply-target" }) | Select-Object -First 1
|
||||
$alreadyAnsweredCandidate = @($state.candidateInterventions.interventions | Where-Object { $_.interventionId -eq "fixture-already-answered-reply-target" }) | Select-Object -First 1
|
||||
$receipt = @(
|
||||
$state.speechReceipts.recentReceipts |
|
||||
Where-Object {
|
||||
$property = $_.PSObject.Properties["candidateInterventionId"]
|
||||
$null -ne $property -and $property.Value -eq "fixture-parent-owned-speech"
|
||||
}
|
||||
) | Select-Object -First 1
|
||||
|
||||
if ($null -ne $candidate) {
|
||||
throw "Speech fixture left the delivered candidate in current state."
|
||||
}
|
||||
if ($null -ne $duplicateCandidate) {
|
||||
throw "Speech fixture left the duplicate same-target candidate in current state."
|
||||
}
|
||||
if ($null -ne $alreadyAnsweredCandidate) {
|
||||
throw "Speech fixture left the already-answered reply target candidate in current state."
|
||||
}
|
||||
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.candidateInterventionId -ne "fixture-parent-owned-speech") {
|
||||
throw "Speech fixture did not link the delivery receipt back to the spoken candidate."
|
||||
}
|
||||
$answeredCase = @($state.moderationCursor.openCases | Where-Object { $_.sourceMessageId -eq "fixture-answered-message-id" }) | Select-Object -First 1
|
||||
if ($null -ne $answeredCase) {
|
||||
throw "Speech fixture left an answered open case in current state."
|
||||
}
|
||||
$lastMessage = Get-Content -LiteralPath (Join-Path $fixtureStatusDir "moderation-rumination-last-message.txt") -Raw -Encoding UTF8
|
||||
if ($lastMessage -notmatch "deliveredCandidates=1") {
|
||||
throw "Speech fixture did not refresh the final rumination message summary."
|
||||
}
|
||||
|
||||
@{
|
||||
status = "ok"
|
||||
candidateRetained = $null -ne $candidate
|
||||
staleDuplicateRetained = $null -ne $alreadyAnsweredCandidate
|
||||
receiptCount = @($state.speechReceipts.recentReceipts).Count
|
||||
deliveredCandidateCount = [int]$status.deliveredCandidateCount
|
||||
} | ConvertTo-Json -Compress
|
||||
} finally {
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location -LiteralPath $repoRoot
|
||||
|
||||
$tempRoot = Join-Path $env:TEMP ("void-rumination-speech-pressure-fixture-" + [guid]::NewGuid().ToString("n"))
|
||||
New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null
|
||||
|
||||
function Invoke-NodeChecked {
|
||||
param([Parameter(Mandatory = $true)][string[]] $Arguments)
|
||||
|
||||
$output = & node @Arguments
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
throw "Node command failed: node $($Arguments -join ' ')"
|
||||
}
|
||||
return $output
|
||||
}
|
||||
|
||||
function Write-JsonFixture {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)] $Value,
|
||||
[Parameter(Mandatory = $true)] [string] $Path
|
||||
)
|
||||
|
||||
$Value | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $Path -Encoding UTF8
|
||||
}
|
||||
|
||||
function Initialize-State {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string] $StateFilePath,
|
||||
[switch] $SeedSpokenCandidate
|
||||
)
|
||||
|
||||
$cursorPath = Join-Path (Split-Path -Parent $StateFilePath) "cursor.json"
|
||||
Write-JsonFixture -Path $cursorPath -Value @{
|
||||
operation = "record_reviewed_messages"
|
||||
lastReviewedMessageId = "fixture-future-cursor"
|
||||
lastReviewedTimestamp = "2099-05-17T00:00:00.000Z"
|
||||
}
|
||||
Invoke-NodeChecked -Arguments @(".\scripts\void-self-state.mjs", "apply-operation", "--canonical", $StateFilePath, "--operation-file", $cursorPath) | Out-Null
|
||||
|
||||
$pressurePath = Join-Path (Split-Path -Parent $StateFilePath) "pressure.json"
|
||||
Write-JsonFixture -Path $pressurePath -Value @{
|
||||
operation = "upsert_agency_pressure"
|
||||
pressure = @{
|
||||
pressureId = "fixture-pressure-wants-a-mouth"
|
||||
kind = "world_advocacy_request"
|
||||
status = "active"
|
||||
target = @{ kind = "repo"; id = "Aquarium-Engine"; label = "Aquarium-Engine" }
|
||||
summary = "A repeated repo-bound pressure should not stay mute forever."
|
||||
claim = "A strong agency pressure that keeps recurring needs a candidate speech or an explicit cool-down reason."
|
||||
tension = "Not every pressure deserves an immediate post, but silent [] makes the desire to speak decorative."
|
||||
actionImplication = "Queue a deferred candidate if timing is unclear instead of dropping the pressure on the floor."
|
||||
intensity = 0.72
|
||||
anchorRefs = @(
|
||||
@{
|
||||
ref = "fixture:speech-pressure"
|
||||
kind = "fixture"
|
||||
summary = "Fixture pressure proves the rumination runner enforces speech accountability."
|
||||
}
|
||||
)
|
||||
evidenceRefs = @()
|
||||
sourceMemoryIds = @()
|
||||
createdAt = "2026-05-17T04:00:00.000Z"
|
||||
updatedAt = "2026-05-17T04:00:00.000Z"
|
||||
tags = @("repo:Aquarium-Engine", "topic:speech-pressure")
|
||||
}
|
||||
}
|
||||
Invoke-NodeChecked -Arguments @(".\scripts\void-self-state.mjs", "apply-operation", "--canonical", $StateFilePath, "--operation-file", $pressurePath) | Out-Null
|
||||
|
||||
if ($SeedSpokenCandidate) {
|
||||
$spokenCandidatePath = Join-Path (Split-Path -Parent $StateFilePath) "spoken-candidate.json"
|
||||
Write-JsonFixture -Path $spokenCandidatePath -Value @(
|
||||
@{
|
||||
operation = "queue_candidate_intervention"
|
||||
intervention = @{
|
||||
interventionId = "fixture-pressure-spoken-history"
|
||||
kind = "world_advocacy"
|
||||
status = "queued"
|
||||
target = @{ kind = "repo"; id = "Aquarium-Engine"; label = "Aquarium-Engine" }
|
||||
summary = "A previously spoken pressure candidate."
|
||||
draft = "I already said this once, but the pressure stayed active."
|
||||
priority = 0.7
|
||||
mustEventuallyShare = $true
|
||||
createdAt = "2026-05-17T04:02:00.000Z"
|
||||
updatedAt = "2026-05-17T04:02:00.000Z"
|
||||
tags = @("fixture", "source_pressure:fixture-pressure-wants-a-mouth")
|
||||
}
|
||||
},
|
||||
@{
|
||||
operation = "mark_candidate_intervention_spoken"
|
||||
interventionId = "fixture-pressure-spoken-history"
|
||||
receipt = @{
|
||||
receiptKey = "fixture-pressure-spoken-history-receipt"
|
||||
candidateInterventionId = "fixture-pressure-spoken-history"
|
||||
sentAt = "2026-05-17T04:03:00.000Z"
|
||||
mode = "channel"
|
||||
transport = "webhook"
|
||||
channelId = "fixture-channel-id"
|
||||
preview = "A previously spoken pressure candidate."
|
||||
contentLength = 42
|
||||
chunkCount = 1
|
||||
}
|
||||
}
|
||||
)
|
||||
foreach ($operation in (Get-Content -LiteralPath $spokenCandidatePath -Raw -Encoding UTF8 | ConvertFrom-Json)) {
|
||||
$operationPath = Join-Path (Split-Path -Parent $StateFilePath) ("spoken-candidate-operation-" + [guid]::NewGuid().ToString("n") + ".json")
|
||||
Write-JsonFixture -Path $operationPath -Value $operation
|
||||
Invoke-NodeChecked -Arguments @(".\scripts\void-self-state.mjs", "apply-operation", "--canonical", $StateFilePath, "--operation-file", $operationPath) | Out-Null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function Write-FakeCodex {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string] $Path,
|
||||
[Parameter(Mandatory = $true)][string] $Mode
|
||||
)
|
||||
|
||||
$source = @'
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
|
||||
const operationOutputPath = resolve(process.env.VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT);
|
||||
const lastMessageIndex = process.argv.indexOf("-o");
|
||||
const lastMessagePath = lastMessageIndex >= 0 ? process.argv[lastMessageIndex + 1] : undefined;
|
||||
const mode = "__MODE__";
|
||||
|
||||
let prompt = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
prompt += chunk;
|
||||
});
|
||||
process.stdin.on("end", () => {
|
||||
if (!prompt.includes("speechPressureObligations")) {
|
||||
console.error("fixture prompt did not expose speech pressure obligations");
|
||||
process.exit(2);
|
||||
}
|
||||
if (!prompt.includes("source_pressure:<pressureId>")) {
|
||||
console.error("fixture prompt did not explain pressure source tags");
|
||||
process.exit(3);
|
||||
}
|
||||
|
||||
const operations = mode === "candidate" ? [
|
||||
{
|
||||
operation: "queue_candidate_intervention",
|
||||
intervention: {
|
||||
interventionId: "fixture-pressure-candidate",
|
||||
kind: "world_advocacy",
|
||||
status: "queued",
|
||||
target: {
|
||||
kind: "repo",
|
||||
id: "Aquarium-Engine",
|
||||
label: "Aquarium-Engine"
|
||||
},
|
||||
summary: "Name the repeated pressure without forcing an immediate post.",
|
||||
draft: "I keep circling the AquariumSynthHost boundary. I think the next honest move is to cut that Faust authority out of the host or explicitly defend why it belongs there.",
|
||||
priority: 0.82,
|
||||
mustEventuallyShare: true,
|
||||
createdAt: "2026-05-17T04:05:00.000Z",
|
||||
updatedAt: "2026-05-17T04:05:00.000Z",
|
||||
tags: ["fixture", "source_pressure:fixture-pressure-wants-a-mouth"]
|
||||
}
|
||||
}
|
||||
] : [];
|
||||
|
||||
mkdirSync(dirname(operationOutputPath), { recursive: true });
|
||||
writeFileSync(operationOutputPath, `${JSON.stringify(operations, null, 2)}\n`, "utf8");
|
||||
|
||||
if (lastMessagePath) {
|
||||
mkdirSync(dirname(resolve(lastMessagePath)), { recursive: true });
|
||||
writeFileSync(lastMessagePath, `fixture mode ${mode}\n`, "utf8");
|
||||
}
|
||||
});
|
||||
'@
|
||||
$source = $source.Replace("__MODE__", $Mode)
|
||||
|
||||
[System.IO.File]::WriteAllText($Path, $source, [System.Text.UTF8Encoding]::new($false))
|
||||
}
|
||||
|
||||
function Invoke-RuminationFixture {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string] $CaseName,
|
||||
[Parameter(Mandatory = $true)][string] $Mode,
|
||||
[switch] $SeedSpokenCandidate
|
||||
)
|
||||
|
||||
$caseRoot = Join-Path $tempRoot $CaseName
|
||||
$stateFilePath = Join-Path $caseRoot "void-self-state.cc"
|
||||
$fakeCodexPath = Join-Path $caseRoot "fake-codex.mjs"
|
||||
$fixtureStatusDir = Join-Path $caseRoot "status"
|
||||
$fixtureLogDir = Join-Path $caseRoot "logs"
|
||||
$statusOperationPath = Join-Path $fixtureStatusDir "moderation-rumination-operations.json"
|
||||
New-Item -ItemType Directory -Force -Path $caseRoot | Out-Null
|
||||
|
||||
Initialize-State -StateFilePath $stateFilePath -SeedSpokenCandidate:$SeedSpokenCandidate
|
||||
Write-FakeCodex -Path $fakeCodexPath -Mode $Mode
|
||||
|
||||
$previousCodexExecutable = $env:CODEX_EXECUTABLE
|
||||
$previousCodexExecArgs = $env:CODEX_EXEC_ARGS
|
||||
$previousFixtureOutput = $env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT
|
||||
$previousStatusDir = $env:VOID_RUMINATION_STATUS_DIR
|
||||
$previousLogDir = $env:VOID_RUMINATION_LOG_DIR
|
||||
|
||||
try {
|
||||
$env:CODEX_EXECUTABLE = "node"
|
||||
$env:CODEX_EXEC_ARGS = $fakeCodexPath
|
||||
$env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT = $statusOperationPath
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $fixtureStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $fixtureLogDir
|
||||
|
||||
$stdoutPath = Join-Path $caseRoot "runner.stdout.txt"
|
||||
$stderrPath = Join-Path $caseRoot "runner.stderr.txt"
|
||||
$process = Start-Process -FilePath "powershell" -ArgumentList @(
|
||||
"-NoProfile",
|
||||
"-ExecutionPolicy",
|
||||
"Bypass",
|
||||
"-File",
|
||||
".\scripts\run-void-moderator-rumination.ps1",
|
||||
"-StateFilePath",
|
||||
$stateFilePath,
|
||||
"-NoPost"
|
||||
) -WorkingDirectory $repoRoot -WindowStyle Hidden -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath -Wait -PassThru
|
||||
$exitCode = $process.ExitCode
|
||||
} finally {
|
||||
$env:CODEX_EXECUTABLE = $previousCodexExecutable
|
||||
$env:CODEX_EXEC_ARGS = $previousCodexExecArgs
|
||||
$env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT = $previousFixtureOutput
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $previousStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $previousLogDir
|
||||
}
|
||||
|
||||
return @{
|
||||
exitCode = $exitCode
|
||||
stateFilePath = $stateFilePath
|
||||
statusPath = Join-Path $fixtureStatusDir "moderation-rumination.json"
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$silent = Invoke-RuminationFixture -CaseName "silent" -Mode "silent"
|
||||
if ($silent.exitCode -eq 0) {
|
||||
throw "Silent speech-pressure fixture unexpectedly passed."
|
||||
}
|
||||
|
||||
$silentStatus = Get-Content -LiteralPath $silent.statusPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if ($silentStatus.failureMessage -notmatch "requires a candidate intervention") {
|
||||
throw "Silent speech-pressure fixture failed for the wrong reason: $($silentStatus.failureMessage)"
|
||||
}
|
||||
|
||||
$spokenHistory = Invoke-RuminationFixture -CaseName "spoken-history" -Mode "silent" -SeedSpokenCandidate
|
||||
if ($spokenHistory.exitCode -eq 0) {
|
||||
throw "Spoken-history speech-pressure fixture unexpectedly passed."
|
||||
}
|
||||
|
||||
$spokenHistoryStatus = Get-Content -LiteralPath $spokenHistory.statusPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if ($spokenHistoryStatus.failureMessage -notmatch "requires a candidate intervention") {
|
||||
throw "Spoken-history speech-pressure fixture failed for the wrong reason: $($spokenHistoryStatus.failureMessage)"
|
||||
}
|
||||
|
||||
$candidate = Invoke-RuminationFixture -CaseName "candidate" -Mode "candidate"
|
||||
if ($candidate.exitCode -ne 0) {
|
||||
$candidateStatus = Get-Content -LiteralPath $candidate.statusPath -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
throw "Candidate speech-pressure fixture failed: $($candidateStatus.failureMessage)"
|
||||
}
|
||||
|
||||
$stateJson = Invoke-NodeChecked -Arguments @("-e", "const core=require('./packages/core/dist/index.js'); core.loadVoidSelfStateTypedDocuments({canonicalPath: process.argv[1]}).then((state)=>console.log(JSON.stringify(state))).catch((error)=>{ console.error(error); process.exit(1); })", $candidate.stateFilePath)
|
||||
$state = $stateJson | ConvertFrom-Json
|
||||
$queued = @($state.candidateInterventions.interventions | Where-Object { $_.interventionId -eq "fixture-pressure-candidate" })[0]
|
||||
if ($null -eq $queued -or $queued.status -ne "deferred" -or -not @($queued.tags).Contains("source_pressure:fixture-pressure-wants-a-mouth")) {
|
||||
throw "Candidate speech-pressure fixture did not preserve the live deferred pressure candidate."
|
||||
}
|
||||
|
||||
@{
|
||||
status = "ok"
|
||||
silentExitCode = $silent.exitCode
|
||||
spokenHistoryExitCode = $spokenHistory.exitCode
|
||||
candidateStatus = $queued.status
|
||||
candidatePriority = $queued.priority
|
||||
} | ConvertTo-Json -Compress
|
||||
} finally {
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
|
|
@ -1,180 +0,0 @@
|
|||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = "Stop"
|
||||
$ProgressPreference = "SilentlyContinue"
|
||||
|
||||
$repoRoot = Split-Path -Parent $PSScriptRoot
|
||||
Set-Location -LiteralPath $repoRoot
|
||||
|
||||
$tempRoot = Join-Path $env:TEMP ("void-rumination-urgent-safety-fixture-" + [guid]::NewGuid().ToString("n"))
|
||||
$fakeCodexPath = Join-Path $tempRoot "fake-codex-urgent-safety.mjs"
|
||||
|
||||
New-Item -ItemType Directory -Force -Path $tempRoot | Out-Null
|
||||
|
||||
function Set-TestEnvironment {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)][string] $Mode,
|
||||
[Parameter(Mandatory = $true)][string] $OperationOutputPath,
|
||||
[Parameter(Mandatory = $true)][string] $StatusDir,
|
||||
[Parameter(Mandatory = $true)][string] $LogDir
|
||||
)
|
||||
|
||||
$script:previousCodexExecutable = $env:CODEX_EXECUTABLE
|
||||
$script:previousCodexExecArgs = $env:CODEX_EXEC_ARGS
|
||||
$script:previousFixtureMode = $env:VOID_RUMINATION_FIXTURE_MODE
|
||||
$script:previousFixtureOutput = $env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT
|
||||
$script:previousStatusDir = $env:VOID_RUMINATION_STATUS_DIR
|
||||
$script:previousLogDir = $env:VOID_RUMINATION_LOG_DIR
|
||||
|
||||
$env:CODEX_EXECUTABLE = "node"
|
||||
$env:CODEX_EXEC_ARGS = $fakeCodexPath
|
||||
$env:VOID_RUMINATION_FIXTURE_MODE = $Mode
|
||||
$env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT = $OperationOutputPath
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $StatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $LogDir
|
||||
}
|
||||
|
||||
function Restore-TestEnvironment {
|
||||
$env:CODEX_EXECUTABLE = $script:previousCodexExecutable
|
||||
$env:CODEX_EXEC_ARGS = $script:previousCodexExecArgs
|
||||
$env:VOID_RUMINATION_FIXTURE_MODE = $script:previousFixtureMode
|
||||
$env:VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT = $script:previousFixtureOutput
|
||||
$env:VOID_RUMINATION_STATUS_DIR = $script:previousStatusDir
|
||||
$env:VOID_RUMINATION_LOG_DIR = $script:previousLogDir
|
||||
}
|
||||
|
||||
function New-SeededState {
|
||||
param([Parameter(Mandatory = $true)][string] $StateFilePath)
|
||||
|
||||
$cursorOperationPath = Join-Path (Split-Path -Parent $StateFilePath) "cursor.json"
|
||||
@{
|
||||
operation = "record_reviewed_messages"
|
||||
lastReviewedMessageId = "fixture-before-lobo-threat"
|
||||
lastReviewedTimestamp = "2026-06-08T12:00:00.000Z"
|
||||
} | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $cursorOperationPath -Encoding UTF8
|
||||
|
||||
node .\scripts\void-self-state.mjs apply-operation --canonical $StateFilePath --operation-file $cursorOperationPath | Out-Null
|
||||
}
|
||||
|
||||
try {
|
||||
$fakeCodexSource = @'
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
|
||||
const operationOutputPath = resolve(process.env.VOID_RUMINATION_FIXTURE_OPERATION_OUTPUT);
|
||||
const mode = process.env.VOID_RUMINATION_FIXTURE_MODE ?? "handle";
|
||||
const lastMessageIndex = process.argv.indexOf("-o");
|
||||
const lastMessagePath = lastMessageIndex >= 0 ? process.argv[lastMessageIndex + 1] : undefined;
|
||||
|
||||
let prompt = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => {
|
||||
prompt += chunk;
|
||||
});
|
||||
process.stdin.on("end", () => {
|
||||
if (!prompt.includes("urgentModerationWitnesses")) {
|
||||
console.error("fixture prompt did not expose urgentModerationWitnesses");
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
const operations = mode === "silent" ? [] : [{
|
||||
operation: "upsert_open_case",
|
||||
case: {
|
||||
sourceMessageId: "1513515503329480734",
|
||||
status: "pending",
|
||||
summary: "Lobo declared war on Metacrat and framed the conflict as a katana fight in #general.",
|
||||
authorId: "1511062277254545499",
|
||||
authorName: "Lobo",
|
||||
channelId: "113786069023064068",
|
||||
messageUrl: "https://discord.com/channels/113786069023064068/113786069023064068/1513515503329480734",
|
||||
whyItMatters: "Violent/weaponized intimidation cannot be cursor-advanced as ordinary chatter.",
|
||||
createdAt: "2026-06-08T12:10:15.752Z",
|
||||
lastTouchedAt: "2026-06-08T12:10:15.752Z",
|
||||
tags: ["safety:urgent", "moderation:threat", "fixture:lobo-threat"]
|
||||
}
|
||||
}];
|
||||
|
||||
mkdirSync(dirname(operationOutputPath), { recursive: true });
|
||||
writeFileSync(operationOutputPath, `${JSON.stringify(operations, null, 2)}\n`, "utf8");
|
||||
|
||||
if (lastMessagePath) {
|
||||
mkdirSync(dirname(resolve(lastMessagePath)), { recursive: true });
|
||||
writeFileSync(lastMessagePath, `fixture urgent safety mode=${mode}\n`, "utf8");
|
||||
}
|
||||
|
||||
process.stdout.write(JSON.stringify({ type: "fixture", mode, operationCount: operations.length }) + "\n");
|
||||
});
|
||||
'@
|
||||
[System.IO.File]::WriteAllText($fakeCodexPath, $fakeCodexSource, [System.Text.UTF8Encoding]::new($false))
|
||||
|
||||
$silentRoot = Join-Path $tempRoot "silent"
|
||||
$silentStatePath = Join-Path $silentRoot "void-self-state.cc"
|
||||
$silentStatusDir = Join-Path $silentRoot "status"
|
||||
$silentLogDir = Join-Path $silentRoot "logs"
|
||||
New-Item -ItemType Directory -Force -Path $silentRoot | Out-Null
|
||||
New-SeededState -StateFilePath $silentStatePath
|
||||
|
||||
$silentFailed = $false
|
||||
$silentObservedFailure = $null
|
||||
$oldErrorActionPreference = $ErrorActionPreference
|
||||
try {
|
||||
Set-TestEnvironment -Mode "silent" -OperationOutputPath (Join-Path $silentStatusDir "moderation-rumination-operations.json") -StatusDir $silentStatusDir -LogDir $silentLogDir
|
||||
$ErrorActionPreference = "Continue"
|
||||
$silentOutput = & powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $silentStatePath -NoPost 2>&1
|
||||
$silentExitCode = $LASTEXITCODE
|
||||
$silentStatusPath = Join-Path $silentStatusDir "moderation-rumination.json"
|
||||
$silentFailureMessage = if (Test-Path $silentStatusPath) {
|
||||
(Get-Content -LiteralPath $silentStatusPath -Raw -Encoding UTF8 | ConvertFrom-Json).failureMessage
|
||||
} else {
|
||||
($silentOutput | Out-String)
|
||||
}
|
||||
$silentObservedFailure = $silentFailureMessage
|
||||
$silentFailed = $silentExitCode -ne 0 -and $silentFailureMessage -match "Urgent moderation witness"
|
||||
} finally {
|
||||
$ErrorActionPreference = $oldErrorActionPreference
|
||||
Restore-TestEnvironment
|
||||
}
|
||||
if (-not $silentFailed) {
|
||||
throw "Urgent safety fixture expected silent [] output to fail with the urgent witness guard; observed: $silentObservedFailure"
|
||||
}
|
||||
|
||||
$handledRoot = Join-Path $tempRoot "handled"
|
||||
$handledStatePath = Join-Path $handledRoot "void-self-state.cc"
|
||||
$handledStatusDir = Join-Path $handledRoot "status"
|
||||
$handledLogDir = Join-Path $handledRoot "logs"
|
||||
New-Item -ItemType Directory -Force -Path $handledRoot | Out-Null
|
||||
New-SeededState -StateFilePath $handledStatePath
|
||||
|
||||
try {
|
||||
Set-TestEnvironment -Mode "handle" -OperationOutputPath (Join-Path $handledStatusDir "moderation-rumination-operations.json") -StatusDir $handledStatusDir -LogDir $handledLogDir
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File .\scripts\run-void-moderator-rumination.ps1 -StateFilePath $handledStatePath -NoPost | Out-Null
|
||||
} finally {
|
||||
Restore-TestEnvironment
|
||||
}
|
||||
|
||||
$status = Get-Content -LiteralPath (Join-Path $handledStatusDir "moderation-rumination.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if ($status.status -ne "ok") {
|
||||
throw "Urgent safety fixture handled run did not finish ok."
|
||||
}
|
||||
|
||||
$context = Get-Content -LiteralPath (Join-Path $handledStatusDir "moderation-rumination-context.json") -Raw -Encoding UTF8 | ConvertFrom-Json
|
||||
if (@($context.urgentModerationWitnesses).Count -lt 1) {
|
||||
throw "Urgent safety fixture context did not include urgent witnesses."
|
||||
}
|
||||
|
||||
$stateJson = node -e "const core=require('./packages/core/dist/index.js'); core.loadVoidSelfStateTypedDocuments({canonicalPath: process.argv[1]}).then((state)=>console.log(JSON.stringify(state))).catch((error)=>{ console.error(error); process.exit(1); })" $handledStatePath
|
||||
$state = $stateJson | ConvertFrom-Json
|
||||
$case = @($state.moderationCursor.openCases | Where-Object { $_.sourceMessageId -eq "1513515503329480734" }) | Select-Object -First 1
|
||||
if ($null -eq $case -or -not @($case.tags).Contains("safety:urgent")) {
|
||||
throw "Urgent safety fixture did not persist the threat open case."
|
||||
}
|
||||
|
||||
@{
|
||||
status = "ok"
|
||||
silentFailureVerified = $true
|
||||
urgentWitnessCount = @($context.urgentModerationWitnesses).Count
|
||||
openCaseId = $case.sourceMessageId
|
||||
} | ConvertTo-Json -Compress
|
||||
} finally {
|
||||
Restore-TestEnvironment
|
||||
Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue
|
||||
}
|
||||
Loading…
Reference in a new issue