Set-StrictMode -Version Latest $ErrorActionPreference = "Stop" $ProgressPreference = "SilentlyContinue" $repoRoot = Split-Path -Parent $PSScriptRoot Set-Location -LiteralPath $repoRoot $tempRoot = Join-Path $env:TEMP ("void-state-boundary-normalization-fixture-" + [guid]::NewGuid().ToString("n")) $stateFilePath = Join-Path $tempRoot "void-self-state.cc" 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-OperationFile { param( [Parameter(Mandatory = $true)][hashtable] $Operation, [Parameter(Mandatory = $true)][string] $Name ) $path = Join-Path $tempRoot $Name $Operation | ConvertTo-Json -Depth 32 | Set-Content -LiteralPath $path -Encoding UTF8 return $path } function Write-NodeScriptFile { param( [Parameter(Mandatory = $true)][string] $Script, [Parameter(Mandatory = $true)][string] $Name ) $path = Join-Path $tempRoot $Name Set-Content -LiteralPath $path -Value $Script -Encoding UTF8 return $path } function Assert-NotFutureTimestamp { param( [Parameter(Mandatory = $true)][string] $Timestamp, [Parameter(Mandatory = $true)][string] $Label ) $parsed = [DateTimeOffset]::Parse($Timestamp) $now = [DateTimeOffset]::UtcNow.AddSeconds(2) if ($parsed -gt $now) { throw "$Label is still in the future: $Timestamp" } } try { $futureMemory = @{ operation = "record_short_term_memory" memory = @{ memoryId = "fixture-future-short-term" kind = "project_seam" target = @{ kind = "repo"; id = "VoidBot"; label = "VoidBot" } summary = "Model-authored timestamps must not become canonical state time." claim = "The parent state service owns canonical timestamps for model-originated writes." tension = "If child timestamps are trusted, projection order can imply events that have not happened." actionImplication = "Stamp short-term memory writes at the typed state boundary." anchorRefs = @( @{ ref = "fixture:future-memory" kind = "fixture" summary = "Future timestamp normalization fixture." } ) evidenceRefs = @() createdAt = "2999-01-01T00:00:00.000Z" updatedAt = "2999-01-01T00:00:00.000Z" tags = @("repo:VoidBot", "topic:state-boundary") } } $targetlessCandidate = @{ operation = "queue_candidate_intervention" intervention = @{ interventionId = "fixture-targetless-queued" kind = "self_advocacy" status = "queued" target = @{ kind = "system"; id = "typed-state-boundary"; label = "Typed state boundary" } summary = "Queued candidate without a delivery target should not project as deliverable." draft = "A queued candidate needs an actual delivery target, or it is theater with a clipboard." priority = 0.5 mustEventuallyShare = $false createdAt = "2999-01-01T00:00:00.000Z" updatedAt = "2999-01-01T00:00:00.000Z" tags = @("topic:state-boundary") } } foreach ($entry in @( @{ Operation = $futureMemory; Name = "future-memory.json" }, @{ Operation = $targetlessCandidate; Name = "targetless-candidate.json" } )) { $path = Write-OperationFile -Operation $entry.Operation -Name $entry.Name Invoke-NodeChecked -Arguments @(".\scripts\void-self-state.mjs", "apply-operation", "--canonical", $stateFilePath, "--operation-file", $path) | Out-Null } $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); })", $stateFilePath) $state = $stateJson | ConvertFrom-Json $memory = @($state.thoughtMemory.shortTerm | Where-Object { $_.memoryId -eq "fixture-future-short-term" })[0] $candidate = @($state.candidateInterventions.interventions | Where-Object { $_.interventionId -eq "fixture-targetless-queued" })[0] $legacyFaceAffectScriptText = @' const core = require(`${process.cwd()}/packages/core/dist/index.js`); const legacy = { schemaVersion: 1, needs: [], socialBonds: [], userStatuses: [{ readId: "fixture-legacy-user-status", target: { kind: "person", id: "metacrat", label: "Metacrat" }, status: "consulted", summary: "Legacy userStatuses should decode as statusReads.", claim: "The live authority is statusReads; userStatuses is only a migration alias.", tension: "A stale persisted key should not keep the Face affect organ offline.", actionImplication: "Decode legacy state and rewrite through the current strict schema.", intensity: 0.6, anchorRefs: [{ ref: "fixture:legacy-userStatuses", kind: "fixture" }], evidenceRefs: [], createdAt: "2026-06-08T00:00:00.000Z", updatedAt: "2026-06-08T00:00:00.000Z", tags: ["fixture:legacy"] }], moodDimensions: [], socialBiases: [], stressResponses: [{ name: "legacy-startle", value: 0.4, updatedAt: "2026-06-08T00:00:00.000Z" }], doctrineStances: [], updatedAt: "2026-06-08T00:00:00.000Z" }; console.log(JSON.stringify(core.voidFaceAffectSchema.parse(legacy))); '@ $legacyFaceAffectScript = Write-NodeScriptFile -Name "legacy-face-affect.cjs" -Script $legacyFaceAffectScriptText $legacyFaceAffectJson = Invoke-NodeChecked -Arguments @($legacyFaceAffectScript) $legacyFaceAffect = $legacyFaceAffectJson | ConvertFrom-Json if ($memory.createdAt -eq "2999-01-01T00:00:00.000Z" -or $memory.updatedAt -eq "2999-01-01T00:00:00.000Z") { throw "Short-term memory kept model-authored future timestamps." } Assert-NotFutureTimestamp -Timestamp $memory.createdAt -Label "short-term createdAt" Assert-NotFutureTimestamp -Timestamp $memory.updatedAt -Label "short-term updatedAt" if ($candidate.status -ne "deferred") { throw "Targetless queued candidate was not deferred; status is $($candidate.status)." } if (-not @($candidate.tags).Contains("normalized:targetless-queued-to-deferred")) { throw "Targetless queued candidate was deferred without a normalization tag." } Assert-NotFutureTimestamp -Timestamp $candidate.createdAt -Label "candidate createdAt" Assert-NotFutureTimestamp -Timestamp $candidate.updatedAt -Label "candidate updatedAt" if (@($legacyFaceAffect.statusReads).Count -ne 1) { throw "Legacy userStatuses did not decode into one statusReads entry." } if ($legacyFaceAffect.PSObject.Properties.Name -contains "userStatuses") { throw "Legacy userStatuses survived parsed Face affect state." } if ($legacyFaceAffect.PSObject.Properties.Name -contains "stressResponses") { throw "Legacy stressResponses survived parsed Face affect state." } @{ status = "ok" shortTermMemoryId = $memory.memoryId candidateStatus = $candidate.status candidateTags = @($candidate.tags) legacyFaceAffectStatusReads = @($legacyFaceAffect.statusReads).Count } | ConvertTo-Json -Compress } finally { Remove-Item -LiteralPath $tempRoot -Recurse -Force -ErrorAction SilentlyContinue }