From 98d576d5ce7a1f94e615140e1fbae74863b5b5c7 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 13 May 2026 19:46:29 +0100 Subject: [PATCH] Harden worker polling and dashboard writes --- apps/worker/src/index.ts | 14 ++++++++++-- scripts/voidbot-operations-dashboard-lib.ps1 | 23 ++++++++++++++++++-- state/evidence.jsonl | 1 + 3 files changed, 34 insertions(+), 4 deletions(-) diff --git a/apps/worker/src/index.ts b/apps/worker/src/index.ts index 813660c..63f0883 100644 --- a/apps/worker/src/index.ts +++ b/apps/worker/src/index.ts @@ -102,9 +102,9 @@ async function main(): Promise { ); providerRegistry = await buildProviderRegistry(systemMessages); console.log(`VoidBot worker polling every ${config.workerPollIntervalMs}ms.`); - await processPendingJobs(); + await pollPendingJobs(); setInterval(() => { - void processPendingJobs(); + void pollPendingJobs(); }, config.workerPollIntervalMs); } @@ -131,6 +131,16 @@ async function processPendingJobs(): Promise { } } +async function pollPendingJobs(): Promise { + try { + await processPendingJobs(); + } catch (error) { + const message = + error instanceof Error ? `${error.name}: ${error.message}` : "Unexpected worker poll failure."; + console.error(`Worker poll failed: ${message}`); + } +} + async function buildProviderRegistry(systemMessagesCatalog: SystemMessageCatalog): Promise { return new ProviderRegistry([ new OwnerCodexProvider({ diff --git a/scripts/voidbot-operations-dashboard-lib.ps1 b/scripts/voidbot-operations-dashboard-lib.ps1 index 60a22f9..245fb07 100644 --- a/scripts/voidbot-operations-dashboard-lib.ps1 +++ b/scripts/voidbot-operations-dashboard-lib.ps1 @@ -112,6 +112,24 @@ function ConvertTo-VoidBotDashboardFileUri { return ([System.Uri]::new($Path)).AbsoluteUri } +function Write-VoidBotDashboardUtf8File { + param( + [Parameter(Mandatory = $true)] + [string] $Path, + [Parameter(Mandatory = $true)] + [AllowEmptyString()] + [string] $Content + ) + + $directory = Split-Path -Parent $Path + if (-not [string]::IsNullOrWhiteSpace($directory)) { + New-Item -ItemType Directory -Force -Path $directory | Out-Null + } + + $encoding = [System.Text.UTF8Encoding]::new($false) + [System.IO.File]::WriteAllText($Path, $Content, $encoding) +} + function New-VoidBotDashboardFileLink { param( [Parameter(Mandatory = $true)] @@ -1452,7 +1470,8 @@ $($checkRows -join [Environment]::NewLine) importState = $importStatePath } } - $snapshot | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $snapshotPath -Encoding utf8 + $snapshotJson = $snapshot | ConvertTo-Json -Depth 10 + Write-VoidBotDashboardUtf8File -Path $snapshotPath -Content $snapshotJson $html = @" @@ -1769,7 +1788,7 @@ $checksSection "@ - Set-Content -LiteralPath $dashboardPath -Value $html -Encoding utf8 + Write-VoidBotDashboardUtf8File -Path $dashboardPath -Content $html return [PSCustomObject]@{ DashboardPath = $dashboardPath diff --git a/state/evidence.jsonl b/state/evidence.jsonl index b1e521e..731039b 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -69,3 +69,4 @@ {"ts":"2026-05-10T15:36:28Z","type":"implementation","status":"ok","note":"Heartbeat repo weather was re-injecting the same four-day commit window on every run because the repo-activity helper had no per-repo cursor. The helper now uses moderation state as an incremental cursor store, persists repo commit cutoffs through the canonical MessagePack-backed state seam, and suppresses already-injected commits by default so repeated sweeps return only genuinely new motion."} {"ts":"2026-05-11T01:27:37Z","type":"implementation","status":"ok","note":"Heartbeat Portuguese mojibake had two sources: the unattended send path was letting human text pass through Windows shell encoding, and PowerShell Get-Content stdout was still mangling UTF-8 moderation surfaces even when the underlying files were fine. Fixed by adding scripts/send-discord-message.ps1 plus BOM-aware content-file reads in scripts/send-discord-message.mjs, and steering the moderation loop through node scripts/export-moderation-context.mjs instead of raw Get-Content for doctrine/state bundles."} {"ts":"2026-05-12T11:47:57Z","type":"implementation","status":"ok","note":"Logon startup failures were dying without a durable transcript and likely firing before Docker finished waking up after reboot. The stack startup script now appends .voidbot/logs/start-voidbot-stack.log, stops the transcript on failure, waits up to 90 seconds for docker version before giving up, and the logon task now runs with an explicit working directory plus a two-minute delay after logon."} +{"ts":"2026-05-13T18:46:07Z","type":"implementation","status":"ok","note":"Worker polling now catches transient claim-loop failures instead of letting a Postgres timeout kill the process, and the operations dashboard writes snapshot/html via explicit UTF-8 file writes so status/dashboard generation stops tripping stack start and watchdog runs with Set-Content stream errors."}