Resolve live loop inspection complaints

This commit is contained in:
Meta Vens 2026-05-17 20:17:28 +01:00
commit c7dbd0e42e
7 changed files with 71 additions and 12 deletions

View file

@ -62,6 +62,7 @@ const moderationRepoCursorSchema = z.object({
const deliveryReceiptSchema = z.object({
receiptKey: nonEmptyStringSchema,
candidateInterventionId: nonEmptyStringSchema.optional(),
sentAt: timestampSchema,
mode: z.string().trim().min(1).optional(),
transport: z.string().trim().min(1).optional(),

View file

@ -273,9 +273,19 @@ function Invoke-CommandWithTimeout {
$stdoutPath = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
$stderrPath = Join-Path ([System.IO.Path]::GetTempPath()) ([System.IO.Path]::GetRandomFileName())
$quotedArguments = @(
$ArgumentList | ForEach-Object {
$argument = [string]$_
if ($argument -notmatch '[\s"]') {
return $argument
}
return '"' + ($argument -replace '(\\*)"', '$1$1\"' -replace '(\\+)$', '$1$1') + '"'
}
)
try {
$process = Start-Process -FilePath $FilePath -ArgumentList $ArgumentList -NoNewWindow -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath -PassThru
$process = Start-Process -FilePath $FilePath -ArgumentList ($quotedArguments -join " ") -NoNewWindow -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath -PassThru
if (-not $process.WaitForExit($TimeoutSeconds * 1000)) {
try {
@ -527,20 +537,34 @@ function Invoke-RemoteLinuxCommand {
[AllowEmptyString()]
[string] $IdentityFile = "",
[Parameter(Mandatory = $true)]
[string] $Command
[string] $Command,
[Parameter(Mandatory = $false)]
[int] $TimeoutSeconds = 45
)
$args = @("-o", "BatchMode=yes", "-o", "ConnectTimeout=10")
$args = @(
"-o", "BatchMode=yes",
"-o", "ConnectTimeout=10",
"-o", "ServerAliveInterval=5",
"-o", "ServerAliveCountMax=2"
)
if (-not [string]::IsNullOrWhiteSpace($IdentityFile)) {
$args += @("-i", $IdentityFile)
}
$args += @($Target, $Command)
$response = & ssh.exe @args
$remoteCommand = "timeout ${TimeoutSeconds}s $Command"
$response = & ssh.exe @($args + @($Target, $remoteCommand)) 2>&1
$exitCode = $LASTEXITCODE
if ($LASTEXITCODE -ne 0) {
throw "SSH command failed for $Target."
if ($exitCode -eq 124) {
throw "SSH command timed out after $TimeoutSeconds second(s) for $Target."
}
if ($exitCode -ne 0) {
$outputText = [string]($response -join "`n")
$output = if ([string]::IsNullOrWhiteSpace($outputText)) { "" } else { " $($outputText.Trim())" }
throw "SSH command failed for $Target.$output"
}
return [string]($response -join "`n")

View file

@ -176,6 +176,7 @@ function Project-SpeechReceiptsForRumination {
@(Convert-ToValueArray -Value $Receipts) | ForEach-Object {
@{
receiptKey = Get-ObjectPropertyString -Value $_ -Name "receiptKey"
candidateInterventionId = Get-ObjectPropertyString -Value $_ -Name "candidateInterventionId"
sent = Project-RelativeTimestamp -Value $_ -Name "sentAt" -Now $Now
mode = Get-ObjectPropertyString -Value $_ -Name "mode"
transport = Get-ObjectPropertyString -Value $_ -Name "transport"

View file

@ -474,7 +474,10 @@ function Assert-AllowedRuminationOperation {
}
function Convert-LastSpeechToReceiptOperation {
param($Speech)
param(
$Speech,
[string] $CandidateInterventionId
)
$sentAt = Get-ObjectPropertyString -Value $Speech -Name "sentAt"
if ($null -eq $Speech -or $null -eq $sentAt) {
@ -483,6 +486,7 @@ function Convert-LastSpeechToReceiptOperation {
$receiptKeyParts = @(
"speech",
$CandidateInterventionId,
$sentAt,
(Get-ObjectPropertyString -Value $Speech -Name "channelId"),
(Get-ObjectPropertyString -Value $Speech -Name "replyToMessageId"),
@ -501,6 +505,10 @@ function Convert-LastSpeechToReceiptOperation {
transport = if (Get-ObjectPropertyString -Value $Speech -Name "transport") { Get-ObjectPropertyString -Value $Speech -Name "transport" } else { "bot" }
}
if (-not [string]::IsNullOrWhiteSpace($CandidateInterventionId)) {
$receipt.candidateInterventionId = $CandidateInterventionId
}
foreach ($key in @("channelId", "replyToMessageId", "personaName", "personaAvatarUrl", "preview", "previewHash")) {
$propertyValue = Get-ObjectPropertyString -Value $Speech -Name $key
if ($null -ne $propertyValue) {
@ -528,7 +536,7 @@ function Convert-LastSpeechToSpokenCandidateOperation {
$Speech
)
$receiptOperation = Convert-LastSpeechToReceiptOperation -Speech $Speech
$receiptOperation = Convert-LastSpeechToReceiptOperation -Speech $Speech -CandidateInterventionId $InterventionId
if ($null -eq $receiptOperation) {
return $null
}
@ -913,7 +921,16 @@ if (-not $NoPost) {
Append-RunLog "writing final rumination status."
$finishedAtUtc = [DateTime]::UtcNow
Append-RunLog "final status timestamp captured."
$lastMessage = if (Test-Path $lastMessagePath) { Get-Content -Path $lastMessagePath -Raw -Encoding UTF8 } else { "" }
$lastMessageSummary = @(
"Void moderation rumination finished."
"mode=" + $(if ($SkipModel) { "typed_rumination_skip_model" } else { "typed_rumination" })
"proposed=" + [string]@($proposedOperations).Count
"applied=" + [string]@($appliedOperations).Count
"deliveredCandidates=" + [string]$deliveredCandidateCount
"finishedAt=" + $finishedAtUtc.ToString("o")
) -join [Environment]::NewLine
[System.IO.File]::WriteAllText($lastMessagePath, $lastMessageSummary + [Environment]::NewLine, [System.Text.UTF8Encoding]::new($false))
$lastMessage = Get-Content -Path $lastMessagePath -Raw -Encoding UTF8
Append-RunLog "final status last message loaded."
$finalStatus = [ordered]@{
@ -937,6 +954,7 @@ $finalStatus = [ordered]@{
stateUpdated = [bool](@($appliedOperations).Count -gt 0)
tracePath = [string]$tracePath
lastMessagePath = [string]$lastMessagePath
lastMessage = [string]$lastMessage
}
Write-JsonFile -Path $statusPath -Data $finalStatus

View file

@ -169,6 +169,13 @@ process.stdout.write(JSON.stringify({ ok: true, mode: "channel", channelId }) +
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."
}
$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"

View file

@ -164,10 +164,17 @@ function Invoke-RemotePowerShell {
$bytes = [System.Text.Encoding]::Unicode.GetBytes($Script)
$encoded = [Convert]::ToBase64String($bytes)
$output = & ssh $Target powershell -NoProfile -NonInteractive -EncodedCommand $encoded
$output = & ssh.exe `
-o BatchMode=yes `
-o ConnectTimeout=10 `
-o ServerAliveInterval=5 `
-o ServerAliveCountMax=2 `
$Target powershell -NoProfile -NonInteractive -EncodedCommand $encoded 2>&1
if ($LASTEXITCODE -ne 0) {
throw "Remote PowerShell command failed on $Target."
$outputText = [string]($output -join "`n")
$detail = if ([string]::IsNullOrWhiteSpace($outputText)) { "" } else { " $($outputText.Trim())" }
throw "Remote PowerShell command failed on $Target.$detail"
}
return ($output -join "`n").Trim()

View file

@ -124,3 +124,4 @@
{"ts":"2026-05-17T18:04:57Z","type":"implementation","status":"ok","note":"Added parent-side short-term rumination clustering: record_short_term_memory now merges repeated same-target/topic pressure into one provisional memory with merged anchors, plus a smoke fixture and prompt guidance. Consolidated the live AquaSynth/Aquarium duplicate cluster from five short-term records to one."}
{"ts":"2026-05-17T18:21:36Z","type":"implementation","status":"ok","note":"Added agency-to-speech accountability: rumination now projects high-intensity active self/world advocacy pressure as speechPressureObligations and rejects silent [] unless the model queues/deferred a candidate or cools/retires the pressure. The fixture proves silent output fails and deferred candidate output passes."}
{"ts":"2026-05-17T18:47:03Z","type":"implementation","status":"ok","note":"Closed the stranded-candidate speech gap. Metacrat's explicit channel permission was observed at 2026-05-17T18:15:24Z, but rumination queued the AquariumSynthHost draft as deferred with no delivery target, then nap skip left it unsent. The runner now treats deferred candidates as unresolved pressure, delivers existing queued candidates with delivery targets, and the stranded candidate was repaired to the permitted channel and posted with a typed receipt."}
{"ts":"2026-05-17T19:15:56Z","type":"implementation","status":"ok","note":"Live-loop inspection complaints resolved: candidate speech receipts now link back to the spoken intervention, rumination report text is refreshed by the runner, watchdog Repixelizer SSH checks no longer hang indefinitely, and offsite sync recovered with a fresh remote archive."}