Tighten Void speech pressure and reply guards

This commit is contained in:
Meta Vens 2026-05-18 09:47:17 +01:00
commit e8acb67141
10 changed files with 225 additions and 30 deletions

View file

@ -379,11 +379,12 @@ function Test-CandidateMatchesPressure {
$Candidate,
[Parameter(Mandatory = $true)]
[string] $PressureId,
[string] $PressureTargetKey
[string] $PressureTargetKey,
[string[]] $AllowedStatuses = @("queued", "spoken")
)
$status = Get-ObjectPropertyString -Value $Candidate -Name "status"
if ($null -eq $status -or -not @("queued", "spoken").Contains($status)) {
if ($null -eq $status -or -not $AllowedStatuses.Contains($status)) {
return $false
}
@ -405,12 +406,78 @@ function Get-DeliverableCandidateInterventions {
$status = Get-ObjectPropertyString -Value $_ -Name "status"
$deliveryTarget = Get-ObjectPropertyValue -Value $_ -Name "deliveryTarget"
$spokenAt = Get-ObjectPropertyString -Value $_ -Name "spokenAt"
$status -eq "queued" -and $null -ne $deliveryTarget -and [string]::IsNullOrWhiteSpace($spokenAt)
$status -eq "queued" -and
$null -ne $deliveryTarget -and
[string]::IsNullOrWhiteSpace($spokenAt) -and
-not (Test-QueuedCandidateAlreadyAnswered -Candidate $_ -Receipts $TypedState.speechReceipts.recentReceipts)
} |
Sort-Object -Property @{ Expression = { [double](Get-ObjectPropertyValue -Value $_ -Name "priority") }; Descending = $true }, @{ Expression = { Get-ObjectPropertyString -Value $_ -Name "updatedAt" }; Descending = $false }
)
}
function Test-CandidateDeliveryTargetMatchesReceipt {
param(
$DeliveryTarget,
$Receipt
)
$channelId = Get-ObjectPropertyString -Value $DeliveryTarget -Name "channelId"
$replyToMessageId = Get-ObjectPropertyString -Value $DeliveryTarget -Name "replyToMessageId"
if ([string]::IsNullOrWhiteSpace($channelId) -or [string]::IsNullOrWhiteSpace($replyToMessageId)) {
return $false
}
return (
(Get-ObjectPropertyString -Value $Receipt -Name "channelId") -eq $channelId -and
(Get-ObjectPropertyString -Value $Receipt -Name "replyToMessageId") -eq $replyToMessageId
)
}
function Test-QueuedCandidateAlreadyAnswered {
param(
$Candidate,
$Receipts
)
$deliveryTarget = Get-ObjectPropertyValue -Value $Candidate -Name "deliveryTarget"
if ($null -eq $deliveryTarget) {
return $false
}
return @(
@(Convert-ToValueArray -Value $Receipts) |
Where-Object { Test-CandidateDeliveryTargetMatchesReceipt -DeliveryTarget $deliveryTarget -Receipt $_ }
).Count -gt 0
}
function Get-AlreadyAnsweredCandidateRetireOperations {
param(
$TypedState,
[Parameter(Mandatory = $true)]
[DateTime] $RetiredAt
)
$receipts = @(Convert-ToValueArray -Value $TypedState.speechReceipts.recentReceipts)
return @(
@(Convert-ToValueArray -Value $TypedState.candidateInterventions.interventions) |
Where-Object {
$status = Get-ObjectPropertyString -Value $_ -Name "status"
$spokenAt = Get-ObjectPropertyString -Value $_ -Name "spokenAt"
$status -eq "queued" -and
[string]::IsNullOrWhiteSpace($spokenAt) -and
(Test-QueuedCandidateAlreadyAnswered -Candidate $_ -Receipts $receipts)
} |
ForEach-Object {
@{
operation = "retire_candidate_intervention"
interventionId = Get-ObjectPropertyString -Value $_ -Name "interventionId"
retiredAt = $RetiredAt.ToString("o")
reason = "duplicate reply target already answered"
}
}
)
}
function Get-SpeechPressureObligations {
param($TypedState)
@ -423,13 +490,13 @@ function Get-SpeechPressureObligations {
$intensity = Get-ObjectPropertyValue -Value $_ -Name "intensity"
$status -in @("active", "ready_to_act") -and
$kind -in @("self_advocacy_request", "world_advocacy_request") -and
$null -ne $intensity -and [double]$intensity -ge 0.65
$null -ne $intensity -and [double]$intensity -ge 0.55
} |
Where-Object {
$pressureId = Get-ObjectPropertyString -Value $_ -Name "pressureId"
$pressureTargetKey = Get-ThoughtTargetKey -Target (Get-ObjectPropertyValue -Value $_ -Name "target")
-not ($candidateInterventions | Where-Object {
Test-CandidateMatchesPressure -Candidate $_ -PressureId $pressureId -PressureTargetKey $pressureTargetKey
Test-CandidateMatchesPressure -Candidate $_ -PressureId $pressureId -PressureTargetKey $pressureTargetKey -AllowedStatuses @("queued")
} | Select-Object -First 1)
} |
ForEach-Object {
@ -446,7 +513,7 @@ function Get-SpeechPressureObligations {
intensity = Get-ObjectPropertyValue -Value $_ -Name "intensity"
sourceMemoryIds = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "sourceMemoryIds"))
tags = @(Convert-ToValueArray -Value (Get-ObjectPropertyValue -Value $_ -Name "tags"))
requiredResolution = "Queue a candidate intervention with tag source_pressure:$((Get-ObjectPropertyString -Value $_ -Name "pressureId")) or cool/retire this pressure with an explicit reason."
requiredResolution = "Queue a live candidate intervention with tag source_pressure:$((Get-ObjectPropertyString -Value $_ -Name "pressureId")) or cool/retire this pressure with an explicit reason. An old spoken candidate does not satisfy a still-active pressure."
}
}
)
@ -983,6 +1050,15 @@ if (-not $NoPost -and -not $SkipModel -and -not $disableRepoCursorAdvance) {
$deliveredCandidateCount = 0
if (-not $NoPost) {
$refreshedTypedContext = Get-TypedSelfState
$alreadyAnsweredCandidateOperations = @(Get-AlreadyAnsweredCandidateRetireOperations -TypedState $refreshedTypedContext.typedState -RetiredAt ([DateTime]::UtcNow))
if ($alreadyAnsweredCandidateOperations.Count -gt 0) {
Append-RunLog ("retiring already-answered queued candidates: {0}" -f $alreadyAnsweredCandidateOperations.Count)
foreach ($operation in $alreadyAnsweredCandidateOperations) {
$appliedOperations += Apply-TypedOperation -Operation $operation
}
$refreshedTypedContext = Get-TypedSelfState
}
$deliverableCandidates = @(Get-DeliverableCandidateInterventions -TypedState $refreshedTypedContext.typedState)
foreach ($candidate in $deliverableCandidates) {
if ($deliveredCandidateCount -ge 1) {

View file

@ -144,8 +144,8 @@ function updateSpeakingPressure({ typedState, previous, sleepCycle, now }) {
.filter((entry) => entry.status === "active" || entry.status === "ready_to_act")
.reduce((sum, entry) => {
const advocacyWeight =
entry.kind === "self_advocacy_request" || entry.kind === "world_advocacy_request" ? 0.22 : 0.12;
const readinessWeight = entry.status === "ready_to_act" ? 1.2 : 1;
entry.kind === "self_advocacy_request" || entry.kind === "world_advocacy_request" ? 0.3 : 0.14;
const readinessWeight = entry.status === "ready_to_act" ? 1.25 : 1.08;
return sum + entry.intensity * advocacyWeight * readinessWeight;
}, 0);
const topThreadPressure = typedState.thoughtMemory.incubation
@ -157,13 +157,13 @@ function updateSpeakingPressure({ typedState, previous, sleepCycle, now }) {
const sleepiness = sleepCycle.isNapping ? 1 : 0;
const priorNeedToSpeak = clamp(previous.needToSpeak ?? 0.35, 0, 1);
const targetNeedToSpeak = clamp(
0.16 + silencePressure * 0.32 + queuedInterventionPressure + topThreadPressure + agencySpeechPressure - recentSpeechDamping * 0.42 - sleepiness * 0.26,
0.18 + silencePressure * 0.32 + queuedInterventionPressure + topThreadPressure + agencySpeechPressure - recentSpeechDamping * 0.38 - sleepiness * 0.26,
0,
1,
);
const needToSpeak = round3(priorNeedToSpeak + (targetNeedToSpeak - priorNeedToSpeak) * 0.42);
const noveltyPressure = round3(
clamp((previous.noveltyPressure ?? 0.35) * 0.62 + topThreadPressure * 0.72 + queuedInterventionPressure * 0.28 + agencySpeechPressure * 0.35, 0, 1),
clamp((previous.noveltyPressure ?? 0.35) * 0.6 + topThreadPressure * 0.72 + queuedInterventionPressure * 0.3 + agencySpeechPressure * 0.44, 0, 1),
);
const confessionPressure = round3(
clamp((previous.confessionPressure ?? 0.25) * 0.68 + silencePressure * 0.12 + sleepiness * 0.1, 0, 1),

View file

@ -90,6 +90,32 @@ process.stdin.on("end", () => {
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 });
@ -151,6 +177,24 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) +
$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
$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
@ -183,15 +227,22 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) +
if ($status.status -ne "ok") {
throw "Speech fixture did not finish ok."
}
if ([int]$status.proposedOperationCount -ne 2 -or [int]$status.appliedOperationCount -ne 3 -or [int]$status.deliveredCandidateCount -ne 1) {
throw "Speech fixture expected two proposals, three applied operations, and one delivered candidate."
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" })[0]
$duplicateCandidate = @($state.candidateInterventions.interventions | Where-Object { $_.interventionId -eq "fixture-duplicate-same-reply-target" })[0]
$receipt = @($state.speechReceipts.recentReceipts)[0]
$alreadyAnsweredCandidate = @($state.candidateInterventions.interventions | Where-Object { $_.interventionId -eq "fixture-already-answered-reply-target" })[0]
$receipt = @(
$state.speechReceipts.recentReceipts |
Where-Object {
$property = $_.PSObject.Properties["candidateInterventionId"]
$null -ne $property -and $property.Value -eq "fixture-parent-owned-speech"
}
)[0]
if ($candidate.status -ne "spoken") {
throw "Speech fixture did not mark the candidate spoken."
@ -199,6 +250,9 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) +
if ($duplicateCandidate.status -ne "retired") {
throw "Speech fixture did not retire the duplicate same-target candidate."
}
if ($alreadyAnsweredCandidate.status -ne "retired") {
throw "Speech fixture did not retire the already-answered reply target candidate."
}
if ($receipt.channelId -ne "fixture-channel-id" -or $receipt.replyToMessageId -ne "fixture-message-id") {
throw "Speech fixture did not preserve the delivery receipt target."
}
@ -213,6 +267,7 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) +
@{
status = "ok"
candidateStatus = $candidate.status
staleDuplicateStatus = $alreadyAnsweredCandidate.status
receiptCount = @($state.speechReceipts.recentReceipts).Count
deliveredCandidateCount = [int]$status.deliveredCandidateCount
} | ConvertTo-Json -Compress

View file

@ -28,7 +28,10 @@ function Write-JsonFixture {
}
function Initialize-State {
param([Parameter(Mandatory = $true)][string] $StateFilePath)
param(
[Parameter(Mandatory = $true)][string] $StateFilePath,
[switch] $SeedSpokenCandidate
)
$cursorPath = Join-Path (Split-Path -Parent $StateFilePath) "cursor.json"
Write-JsonFixture -Path $cursorPath -Value @{
@ -66,6 +69,48 @@ function Initialize-State {
}
}
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 {
@ -138,7 +183,8 @@ process.stdin.on("end", () => {
function Invoke-RuminationFixture {
param(
[Parameter(Mandatory = $true)][string] $CaseName,
[Parameter(Mandatory = $true)][string] $Mode
[Parameter(Mandatory = $true)][string] $Mode,
[switch] $SeedSpokenCandidate
)
$caseRoot = Join-Path $tempRoot $CaseName
@ -149,7 +195,7 @@ function Invoke-RuminationFixture {
$statusOperationPath = Join-Path $fixtureStatusDir "moderation-rumination-operations.json"
New-Item -ItemType Directory -Force -Path $caseRoot | Out-Null
Initialize-State -StateFilePath $stateFilePath
Initialize-State -StateFilePath $stateFilePath -SeedSpokenCandidate:$SeedSpokenCandidate
Write-FakeCodex -Path $fakeCodexPath -Mode $Mode
$previousCodexExecutable = $env:CODEX_EXECUTABLE
@ -204,6 +250,16 @@ try {
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
@ -220,6 +276,7 @@ try {
@{
status = "ok"
silentExitCode = $silent.exitCode
spokenHistoryExitCode = $spokenHistory.exitCode
candidateStatus = $queued.status
candidatePriority = $queued.priority
} | ConvertTo-Json -Compress