VoidBot/scripts/smoke-void-rumination-speech-pressure-fixture.ps1
2026-05-22 20:45:39 +01:00

285 lines
12 KiB
PowerShell

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
}