diff --git a/docs/architecture/voidbot-provider-advertisements.fixture.json b/docs/architecture/voidbot-provider-advertisements.fixture.json index 7debecb..b4e5863 100644 --- a/docs/architecture/voidbot-provider-advertisements.fixture.json +++ b/docs/architecture/voidbot-provider-advertisements.fixture.json @@ -379,9 +379,10 @@ { "id": "voidbot.operations_health", "path": ".voidbot/status/operations-health.json", - "owner": "VoidBot watchdog", + "owner": "VoidBot operations probe", "contains": [ - "debug status only" + "debug status only", + "lifecycle owned by Idunn" ] } ], diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index ac487b3..f79bf1c 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -17,7 +17,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou ## Current Shape - Discord bot and worker are live. -- The unattended repo Persona / CTB swarm is live again as of 2026-05-21 after the teardown/restart pass. `GameCult Local Orchestrator` is enabled and owns the single hidden pulse for repo Persona CTB turns, Void mood drift, Void moderation rumination, and the watchdog. Bifrost dispatch is present but disabled by default behind `BIFROST_DISPATCH_ENABLED=false`. +- The unattended repo Persona / CTB swarm is live again as of 2026-05-21 after the teardown/restart pass. `GameCult Local Orchestrator` is enabled and owns the single hidden pulse for repo Persona CTB turns, Void mood drift, and Void moderation rumination. Odin's Idunn keepalive organ owns watchdog recovery and operator escalation for stale VoidBot service health. Bifrost dispatch is present but disabled by default behind `BIFROST_DISPATCH_ENABLED=false`. - The teardown authority remains `notes/agent-swarm-teardown-plan-2026-05-21.md`, but it is now a restart checklist and smell ledger rather than a stop sign. Keep using it when a Persona/Bifrost/CTB boundary smells like prompt/parser/transport Jenga. - The repo-controlled pause flag is `state/agent-swarm-paused.json`. While it is missing or has `"paused": false`, swarm runners may operate. While it has `"paused": true` or is malformed, the Persona heartbeat runner skips and the orchestrator skips agent-swarm organs. This is the brake. Do not route around it. - Postgres owns jobs, audit events, interaction memory, and rate-limit state. @@ -36,7 +36,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou - Prompt-facing rumination chronology is projected by `scripts/lib/void-rumination-context-projection.ps1`. Exact timestamps stay in typed state/status/cursors; the child context gets relative phrases like `5 minutes ago`, including recent Discord messages, repo activity, receipts, memory timestamps, and sleep/speech timing. - `scripts/simulate-void-mood.mjs` owns typed sleep/speaking-pressure over `.voidbot/private/void-self-state.cc`. During sleep, it invokes typed memory maintenance once per nap unless that nap already completed a pass; use `--skip-memory-maintenance` for smoke runs that must not call Codex. - `npm run moderation:memory-maintenance:sleep-fixture` now proves the non-skip sleep path without spending a live model call: it seeds a temporary `.cc`, forces mood drift through sleep maintenance, uses a fake Codex child to propose `apply_memory_distillation`, and verifies short-term residue is gone while the durable memory keeps subject, claim, tension, action implication, and anchor. -- The live local pulse is now one hidden scheduled task: `GameCult Local Orchestrator`. It invokes repo Persona CTB heartbeats, Void mood drift, Void moderation rumination, and the operations watchdog with one lock, one status surface, per-organ logs, and per-organ timeouts. The Bifrost dispatch organ remains wired but is skipped unless `BIFROST_DISPATCH_ENABLED=true`. The old per-organ tasks are disabled and should remain disabled unless testing one organ in isolation. +- The live local swarm/moderation/mood pulse is the hidden scheduled task `GameCult Local Orchestrator`. It invokes repo Persona CTB heartbeats, Void mood drift, and Void moderation rumination with one lock, one status surface, per-organ logs, and per-organ timeouts. The Bifrost dispatch organ remains wired but is skipped unless `BIFROST_DISPATCH_ENABLED=true`. Watchdog recovery is no longer a VoidBot orchestrator organ; Idunn watches `npm run state:check` through `E:\Projects\Odin\scripts\health-voidbot.cmd` and restarts through `E:\Projects\Odin\scripts\restart-voidbot.cmd`. - `scripts/start-voidbot-stack.ps1` now treats the local Bifrost container stack as a startup dependency. It reads `BIFROST_ROOT` and `BIFROST_HEALTH_URL`, runs `E:\Projects\Bifrost\compose.local.yaml` when the health endpoint is down, and records the result under `runtime-stack.json` as `bifrost`. - `npm run moderation:rumination:fixture` proves the non-skip typed rumination branch without spending a live model call: it seeds a temporary `.cc`, routes the runner through a fake Codex child, and verifies short-term memory, incubation, agency pressure, and candidate intervention proposals are parent-applied typed operations with no exact timestamp leakage in the prompt-facing context. - `npm run moderation:rumination:bifrost-transport-fixture` proves queued speech and moderation status DMs cross the required Bifrost bridge path and return receipts to typed state. `npm run moderation:rumination:speech-fixture` proves the parent-owned candidate closure path with a fake Bifrost bridge. The child prompt forbids direct send-script calls. @@ -67,7 +67,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou - Source archive storage is no longer one giant JSON monolith; `packages/rag/src/source-document-archive.ts` now keeps a small manifest at `.voidbot/rag/source-documents.json` and per-repo shards under `.voidbot/rag/source-documents.repos/`. - Ops health, local backup verification, and dashboard surfaces already exist. Raven-backed offsite sync is disabled by config and should not be treated as a live health requirement. - The stack has a dedicated logon startup task path now, so a reboot does not have to mean manual necromancy for bot and worker. -- The operations watchdog now supports ignored local extension checks, so adjacent-service fire signals can ride the same owner DM alert path without hardcoding private project gossip into the public repo. +- The operations health probe now supports ignored local extension checks, so adjacent-service fire signals can become probe issues without hardcoding private project gossip into the public repo. Idunn owns the decision to restart or escalate to the operator. - Source repo discovery now reconciles automatically during `npm run stack:start` and through the watchdog path, so new repos under `SOURCE_REPO_ROOT` stop waiting for manual hook folklore before they get indexed. - Bot-directed prompts now stay in the raw Discord archive but are excluded from default semantic history indexing and lexical fallback, so repeated summons stop teaching the bot its own call-and-response loops. - Source-grounding hints are advisory now; the owner and local lanes can decide when repo or lore tool use is actually warranted instead of obeying a lexical front-door veto. diff --git a/notes/voidbot-current-system-map.md b/notes/voidbot-current-system-map.md index 9bbab5b..ec0841a 100644 --- a/notes/voidbot-current-system-map.md +++ b/notes/voidbot-current-system-map.md @@ -116,7 +116,7 @@ Verse-facing service contract: `docs/architecture/voidbot-verse-service-contract - `scripts/install-stack-startup-task.ps1` - installs the hidden logon task that runs `start-voidbot-stack.ps1` automatically after reboot or sign-in. - `scripts/check-voidbot-operations.ps1` - - watchdog for process liveness, Qdrant, Postgres, Ollama, source-repo reconcile drift, Discord auth, backup freshness, offsite sync freshness, and optional ignored local extension checks. + - local operations health probe for process liveness, Qdrant, Postgres, Ollama, source-repo reconcile drift, Discord auth, backup freshness, offsite sync freshness, and optional ignored local extension checks. Odin's Idunn keepalive organ owns the recurring watchdog loop, restart authority, and operator escalation. - `scripts/run-void-moderator-rumination.ps1` - typed rumination runner. It builds a bounded context packet from `.voidbot/private/void-self-state.cc`, recent Discord chronology, and read-only repo activity; loads `prompts/void-moderator-rumination.md`; runs bounded Codex rumination; applies returned typed operations through `scripts/void-self-state.mjs`; then records parent-owned cursor and speech receipts. After parent-owned candidate delivery, it verifies the receipt is linked to the candidate and the candidate is marked `spoken`. - `scripts/lib/void-rumination-context-projection.ps1` @@ -143,7 +143,7 @@ Verse-facing service contract: `docs/architecture/voidbot-verse-service-contract - `scripts/install-moderation-rumination-task.ps1` - legacy installer for isolating the moderation/participation loop. The live scheduled pulse is now `GameCult Local Orchestrator`; keep this per-organ task disabled unless testing the organ in isolation. - `scripts/run-gamecult-orchestrator.ps1` - - local pulse owner for agent transport/runtime chores. It runs Bifrost dispatch, repo Persona CTB heartbeats, Void mood drift, Void moderation rumination, and the operations watchdog from one hidden scheduled task with one lock, per-organ status/log files, and per-organ timeouts. + - local pulse owner for agent transport/runtime chores. It runs Bifrost dispatch, repo Persona CTB heartbeats, Void mood drift, and Void moderation rumination from one hidden scheduled task with one lock, per-organ status/log files, and per-organ timeouts. It no longer owns operations watchdog recovery. - `scripts/install-gamecult-orchestrator-task.ps1` - installs scheduled task `GameCult Local Orchestrator` through the hidden PowerShell launcher and can disable the old per-organ tasks: `Bifrost Agent Dispatch`, `VoidBot Repo Persona Heartbeats`, `Void Mood Drift`, `Void Moderator Rumination`, and `VoidBot Operations Watchdog`. - Scheduled organ `Void Mood Drift` diff --git a/scripts/check-voidbot-operations.ps1 b/scripts/check-voidbot-operations.ps1 index f3c8f76..6b4e632 100644 --- a/scripts/check-voidbot-operations.ps1 +++ b/scripts/check-voidbot-operations.ps1 @@ -586,6 +586,22 @@ function Test-ConfigEnabled { return $true } +function Get-IdunnLocalKeepaliveStatus { + $taskName = "Idunn Local Keepalive" + $task = Get-ScheduledTask -TaskName $taskName -ErrorAction SilentlyContinue + $taskEnabled = $null -ne $task -and [bool]$task.Settings.Enabled + $startupPath = Join-Path ([Environment]::GetFolderPath("Startup")) "$taskName.cmd" + $startupInstalled = Test-Path -LiteralPath $startupPath + + return [pscustomobject]@{ + taskName = $taskName + taskEnabled = $taskEnabled + startupPath = $startupPath + startupInstalled = $startupInstalled + installed = $taskEnabled -or $startupInstalled + } +} + function Invoke-RemotePowerShellJson { param( [Parameter(Mandatory = $true)] @@ -1307,22 +1323,38 @@ try { $watchdogEnabled = [bool]$watchdogTask.Settings.Enabled $orchestratorTask = Get-ScheduledTask -TaskName "GameCult Local Orchestrator" -ErrorAction SilentlyContinue $orchestratorEnabled = $null -ne $orchestratorTask -and [bool]$orchestratorTask.Settings.Enabled - $watchdogTaskStatus = if ($watchdogEnabled -or $orchestratorEnabled) { "passed" } else { "failed" } + $idunnKeepalive = Get-IdunnLocalKeepaliveStatus + $watchdogTaskStatus = if ($idunnKeepalive.installed -or $watchdogEnabled) { "passed" } else { "failed" } $watchdogTaskDetail = if ($watchdogEnabled) { - "Operations watchdog task is installed and enabled." - } elseif ($orchestratorEnabled) { - "Operations watchdog task is disabled because GameCult Local Orchestrator owns the watchdog pulse." + "Legacy operations watchdog task is still installed and enabled." + } elseif ($idunnKeepalive.installed) { + "Operations watchdog task is disabled because Idunn Local Keepalive owns watchdog recovery and escalation." } else { - "Operations watchdog task is installed but disabled, and GameCult Local Orchestrator is not enabled." + "Operations watchdog task is installed but disabled, and Idunn Local Keepalive is not enabled." } Add-Check -Name "scheduled_task.watchdog" -Status $watchdogTaskStatus -Detail $watchdogTaskDetail -Data @{ taskName = $watchdogTaskName state = [string]$watchdogTask.State orchestratorTaskName = "GameCult Local Orchestrator" orchestratorEnabled = $orchestratorEnabled + idunnTaskName = $idunnKeepalive.taskName + idunnTaskEnabled = $idunnKeepalive.taskEnabled + idunnStartupPath = $idunnKeepalive.startupPath + idunnStartupInstalled = $idunnKeepalive.startupInstalled } } catch { - Add-Check -Name "scheduled_task.watchdog" -Status "warning" -Detail "Operations watchdog task $watchdogTaskName is not installed yet." + $idunnKeepalive = Get-IdunnLocalKeepaliveStatus + if ($idunnKeepalive.installed) { + Add-Check -Name "scheduled_task.watchdog" -Status "passed" -Detail "VoidBot legacy operations watchdog task is absent because Idunn Local Keepalive owns watchdog recovery and escalation." -Data @{ + taskName = $watchdogTaskName + idunnTaskName = $idunnKeepalive.taskName + idunnTaskEnabled = $idunnKeepalive.taskEnabled + idunnStartupPath = $idunnKeepalive.startupPath + idunnStartupInstalled = $idunnKeepalive.startupInstalled + } + } else { + Add-Check -Name "scheduled_task.watchdog" -Status "warning" -Detail "Operations watchdog task $watchdogTaskName is not installed, and Idunn Local Keepalive is not enabled." + } } if (-not $offsiteBackupEnabled) { diff --git a/scripts/export-voidbot-provider-advertisements.mjs b/scripts/export-voidbot-provider-advertisements.mjs index 89f8ed4..4d0b6b1 100644 --- a/scripts/export-voidbot-provider-advertisements.mjs +++ b/scripts/export-voidbot-provider-advertisements.mjs @@ -77,7 +77,7 @@ function buildCatalog() { witnesses: [ witness("voidbot.discord_archive", ".voidbot/rag/messages.json", "VoidBot raw archive", ["raw archive", "forensics"]), witness("voidbot.history_vectors", "qdrant://voidbot/history", "Qdrant", ["semantic history vectors"]), - witness("voidbot.operations_health", ".voidbot/status/operations-health.json", "VoidBot watchdog", ["debug status only"]), + witness("voidbot.operations_health", ".voidbot/status/operations-health.json", "VoidBot operations probe", ["debug status only", "lifecycle owned by Idunn"]), ], commands: [ command("archive.search_history", "read-only", "Search archived Discord history through native CultMesh/CultNet service commands; MCP is external-agent compatibility."), diff --git a/scripts/install-operations-watchdog-task.ps1 b/scripts/install-operations-watchdog-task.ps1 index b0cdc43..2776f67 100644 --- a/scripts/install-operations-watchdog-task.ps1 +++ b/scripts/install-operations-watchdog-task.ps1 @@ -68,21 +68,13 @@ if ($intervalMinutesValue -lt 15) { throw "VOIDBOT_HEALTHCHECK_INTERVAL_MINUTES must be at least 15." } -if (-not ($config.ContainsKey("DISCORD_BOT_TOKEN")) -or [string]::IsNullOrWhiteSpace($config["DISCORD_BOT_TOKEN"])) { - throw "DISCORD_BOT_TOKEN must be configured in .env before installing the watchdog task." -} - -if (-not ($config.ContainsKey("DISCORD_OWNER_ID")) -or [string]::IsNullOrWhiteSpace($config["DISCORD_OWNER_ID"])) { - throw "DISCORD_OWNER_ID must be configured in .env before installing the watchdog task." -} - $startAt = (Get-Date).AddMinutes(5) # Use WSH as a GUI host so the interactive task can stay hidden without flashing a console. -$action = New-ScheduledTaskAction -Execute "wscript.exe" -Argument "//B //nologo `"$hiddenLauncher`" -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$checkScript`" -NotifyOwner -FailOnIssues" +$action = New-ScheduledTaskAction -Execute "wscript.exe" -Argument "//B //nologo `"$hiddenLauncher`" -NoProfile -NonInteractive -WindowStyle Hidden -ExecutionPolicy Bypass -File `"$checkScript`" -FailOnIssues" $trigger = New-ScheduledTaskTrigger -Once -At $startAt -RepetitionInterval (New-TimeSpan -Minutes $intervalMinutesValue) -RepetitionDuration (New-TimeSpan -Days 3650) $settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Minutes 15) $principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Limited -$description = "Runs the VoidBot operations watchdog on a repeating interval and DMs the owner when the stack or backup path drifts into the swamp." +$description = "Runs the legacy VoidBot operations probe. Idunn owns watchdog recovery and operator escalation." Register-ScheduledTask -TaskName $taskNameValue -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Description $description -Force | Out-Null @@ -92,6 +84,7 @@ Write-Host "Task runs as: $($env:USERDOMAIN)\$($env:USERNAME)" Write-Host "Logon mode: Interactive" Write-Host "Launcher: wscript.exe hidden PowerShell shim" Write-Host "Execution time limit: 15 minutes" +Write-Host "Operator escalation: owned by Idunn Local Keepalive, not this legacy probe task" if ($RunNow) { Start-ScheduledTask -TaskName $taskNameValue diff --git a/scripts/run-gamecult-orchestrator.ps1 b/scripts/run-gamecult-orchestrator.ps1 index f704e4c..ff2f84f 100644 --- a/scripts/run-gamecult-orchestrator.ps1 +++ b/scripts/run-gamecult-orchestrator.ps1 @@ -319,15 +319,6 @@ try { Cwd = $repoRoot Executable = $powershell Arguments = @("-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", (Join-Path $PSScriptRoot "run-void-moderator-rumination.ps1")) - }, - [pscustomobject]@{ - Id = "voidbot-operations-watchdog" - Label = "VoidBot operations watchdog" - IntervalMinutes = Get-ConfigInt -Config $config -Name "VOIDBOT_HEALTHCHECK_INTERVAL_MINUTES" -Default 60 -Minimum 15 - TimeoutMinutes = 5 - Cwd = $repoRoot - Executable = $powershell - Arguments = @("-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-File", (Join-Path $PSScriptRoot "check-voidbot-operations.ps1"), "-NotifyOwner", "-FailOnIssues") } ) diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 2711531..45ad5aa 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -391,3 +391,4 @@ {"ts":"2026-06-07T00:22:44Z","type":"implementation","status":"ok","note":"Audited Face-to-Persona migration and cut remaining active public-agent Face vocabulary. MCP identity listings now return personas/personaCount, worker metadata/logs use repo-persona/persona-interpreter/author-kind persona, birth/status artifacts use voidbot-persona-birth.*, dashboard ids/HUD use Persona labels, docs point at void.persona_affect and read_repo_persona_* tools, and smokes/projected prompts verify the Persona path."} {"ts":"2026-06-07T00:26:33Z","type":"implementation","status":"ok","note":"Full prompt-level Face-to-Persona audit found the remaining deprecated Face vocabulary came from the global Codex AGENTS.md injected into repo Persona prompts. Patched the global doctrine so the public/social faculty and Persona State Standard use Persona/repo Persona/public Persona terminology, then reassembled a Nibu prompt with no deprecated Face hits."} {"ts":"2026-06-07T00:28:37Z","type":"implementation","status":"ok","note":"The live private repo Persona registry still contained Nibu description text and channel posture using Face terminology. Mechanically rewrote those private registry strings to Persona terminology and reassembled Nibu's prompt; the remaining Face scan no longer reports deprecated public-agent vocabulary from the prompt."} +{"ts":"2026-06-07T01:37:24Z","type":"implementation","status":"ok","note":"Moved VoidBot watchdog ownership to Odin/Idunn: GameCult Local Orchestrator no longer runs check-voidbot-operations with NotifyOwner, Idunn watches health-voidbot.cmd and can run restart-voidbot.cmd, and the first Idunn-owned VoidBot probe completed healthy with 34 passed / 0 warnings / 0 failures."} diff --git a/state/map.yaml b/state/map.yaml index bb26b76..1178ebf 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -61,7 +61,7 @@ active_subgoals: - Treat `notes/agent-swarm-teardown-plan-2026-05-21.md` as the historical teardown authority and the restart checklist. The repo Persona swarm is live again, but only because the scheduler, prompt, model-output logs, parent Interpreter, Bifrost routing, and worker side-effect rails now have explicit observable boundaries. Keep watching for prompt/parser/transport Jenga before adding new swarm features. - Make the new project-state surfaces the default continuity path for nontrivial VoidBot work. - Keep the rebuilt moderation/self-state foundation on canonical CultCache `.cc` typed operations, never an editable JSON working projection. - - Keep the typed moderation, mood, repo Persona, and watchdog organs behind the single `GameCult Local Orchestrator` scheduled pulse; the per-organ scheduled tasks are legacy installer surfaces and should stay disabled unless a deliberate isolation test needs one. Bifrost dispatch is disabled by default behind `BIFROST_DISPATCH_ENABLED=false` until the governance bridge is rebuilt cleanly. As of the 2026-05-21 restart, the orchestrator task is enabled and is the live swarm pulse owner. + - Keep the typed moderation, mood, and repo Persona organs behind the single `GameCult Local Orchestrator` scheduled pulse; watchdog recovery and operator escalation now belong to Odin's Idunn keepalive organ. The per-organ scheduled tasks are legacy installer surfaces and should stay disabled unless a deliberate isolation test needs one. Bifrost dispatch is disabled by default behind `BIFROST_DISPATCH_ENABLED=false` until the governance bridge is rebuilt cleanly. As of the 2026-05-21 restart, the orchestrator task is enabled and is the live swarm pulse owner. - Keep legacy moderation mirrors, whole-state projection commits, and deleted memory-organ compensators from re-entering the active path before adding more moderation, mood, agency, or public-lane behavior. - Keep public-lane limits, moderation, and provider behavior constrained enough that one 4060 Ti box does not get flayed for sport. - Preserve boring observability and startup discipline around Postgres, Qdrant, Ollama, Docker, backups, scheduled tasks, watchdogs, and any private local fire-signal extensions. @@ -200,7 +200,7 @@ landed_baseline: - The stack now has a dedicated logon startup task path so bot and worker can come back after a reboot without waiting for manual resurrection. - Interaction memory and behavioral profiling are live. - Tool-driven retrieval is live, and source-grounding hints now bias the reply lanes instead of hard-gating them with a lexical pre-classifier. - - The operations watchdog can now load ignored local extension checks, so adjacent-service fire signals can piggyback on the owner DM path without turning the public repo into cross-project gossip. + - The operations health probe can now load ignored local extension checks, so adjacent-service fire signals can become typed probe issues without turning the public repo into cross-project gossip. Idunn owns recovery and operator escalation when those issues need action. - The owner Codex lane is now split into orchestration, runtime/parsing, shared helpers, and rendering modules instead of one swollen provider file. - The local Ollama lane is now split into orchestration, prompt/render helpers, tool-loop helpers, and shared types/constants instead of one swollen provider file. - The Discord bot lane is now split into a gateway shell plus dedicated command-handler and support helper modules instead of one swollen entrypoint file. @@ -225,7 +225,7 @@ landed_baseline: - Long-term memory is durable but plastic. `revise_durable_memory`, `retire_durable_memory`, and `crystallize_memory_into_identity` make revision, contradiction/obsolescence, and self-doctrine crystallization explicit instead of silently overwriting older thoughts. - `npm run moderation:memory-lifecycle:fixture` verifies memory lifecycle flow: short-term promotion, durable revision, superseded-memory retirement, and crystallization into one active identity memory plus self-profile value. - The sleep-maintenance path has a model-branch fixture: `npm run moderation:memory-maintenance:sleep-fixture` seeds a temporary `.cc`, forces mood drift into sleep maintenance, routes the non-skip Codex branch through a fake child, and verifies short-term memory is consumed into one durable meaning-preserving record. - - The local `GameCult Local Orchestrator` scheduled task is installed as the hidden one-minute pulse for Bifrost dispatch, repo Persona CTB heartbeats, Void mood drift, Void moderation rumination, and the operations watchdog. It writes per-organ logs under `.voidbot/logs/orchestrator`, summary/status under `.voidbot/status/gamecult-orchestrator*.json`, freezes overlap with one orchestrator lock, and enforces per-organ timeouts so a hung child cannot occupy the whole clock forever. + - The local `GameCult Local Orchestrator` scheduled task is installed as the hidden one-minute pulse for Bifrost dispatch, repo Persona CTB heartbeats, Void mood drift, and Void moderation rumination. It writes per-organ logs under `.voidbot/logs/orchestrator`, summary/status under `.voidbot/status/gamecult-orchestrator*.json`, freezes overlap with one orchestrator lock, and enforces per-organ timeouts so a hung child cannot occupy the whole clock forever. Idunn owns the separate watchdog keepalive loop for VoidBot process health, stale recovery, restart authority, and operator escalation. - VoidBot stack startup now treats Bifrost's local Docker Compose stack as a dependency. `scripts/start-voidbot-stack.ps1` checks `BIFROST_HEALTH_URL`, runs `BIFROST_ROOT/compose.local.yaml` when needed, and records `runtime-stack.json.bifrost` before starting the bot and worker. - The typed rumination path now has a model-branch fixture: `npm run moderation:rumination:fixture` seeds a temporary `.cc`, routes the non-skip Codex branch through a fake child, and verifies short-term memory, incubation, agency-pressure, and candidate-intervention proposals cross only through parent-applied typed operations without leaking exact timestamps into the prompt-facing context. - The typed rumination speech path is parent-owned now. Candidate interventions can carry an optional delivery target; the child may queue candidates but must not call Discord send scripts. The parent runner delivers at most one newly queued deliverable candidate per pass through Bifrost, records a delivery receipt, and marks the candidate spoken through `mark_candidate_intervention_spoken`. `npm run moderation:rumination:bifrost-transport-fixture` covers queued speech plus moderation status DMs, and `npm run moderation:rumination:speech-fixture` covers the parent-owned candidate closure path with a fake Bifrost bridge. @@ -256,5 +256,5 @@ rejected_paths: reason: sleep/distillation that collapses concrete claims into generic abstraction creates memory nonsense and then forces later cleanup compensators current_status: - summary: VoidBot's bot/worker/provider/RAG/Postgres/Qdrant/CultCache structure is live, and the repo Persona swarm active-stack fault has been repaired. The worker had died, and a direct restart exposed that `claimRunnableJobs()` batch-claimed the approved backlog while processing it sequentially, making many Personas appear active even though only one turn could move. The worker now claims one runnable job per poll, and heartbeat reconciliation treats `running` jobs as active while `approved` jobs are queued blockers only. Direct worker PID 26720 is running after the self-transcript tool pass; visible active count dropped to one. Public speech is still parent-Interpreter owned, silence/code-fence transport guards remain in place, GameCult Local Orchestrator remains the pulse owner, and Bifrost work routing remains paused by default. + summary: VoidBot's bot/worker/provider/RAG/Postgres/Qdrant/CultCache structure is live, and the repo Persona swarm active-stack fault has been repaired. Public speech is still parent-Interpreter owned, silence/code-fence transport guards remain in place, GameCult Local Orchestrator remains the swarm/moderation/mood pulse owner, Idunn owns watchdog recovery and operator escalation for stale VoidBot service health, and Bifrost work routing remains paused by default. next_action: Watch the current single running repo Persona job and the remaining approved backlog drain. If chat motion still looks absent, distinguish ordinary Interpreter `say=0` decisions and idle cooling from transport failure; investigate the separate CultCache/CultMesh decode error (`Extra ... byte(s) found at buffer[1]`) if it recurs on a `say=1` job or before a Discord bridge artifact is written.