Delete Windows omnibus scheduler

This commit is contained in:
Meta Vens 2026-07-16 06:07:08 +01:00
commit e2187928a6
14 changed files with 34 additions and 742 deletions

View file

@ -630,7 +630,7 @@
"providerId": "voidbot.swarm",
"verseId": "voidbot.local",
"title": "VoidBot Swarm",
"description": "CTB initiative order, active turns, pause/heat/cadence controls, orchestrator organ health, and selected Face state witness.",
"description": "CTB initiative order, active turns, pause/heat/cadence controls, daemon health, and selected Face state witness.",
"status": "live",
"endpoint": "cultmesh://voidbot.local/eve/providers/voidbot.swarm",
"documents": [
@ -724,14 +724,6 @@
"derived debug/status projection",
"not mutation authority"
]
},
{
"id": "voidbot.orchestrator_state",
"path": ".voidbot/status/gamecult-orchestrator.json",
"owner": "GameCult Local Orchestrator",
"contains": [
"debug/status source"
]
}
],
"owners": {

View file

@ -50,11 +50,10 @@ VoidBot surfaces as they become first-class Verse nodes.
`.voidbot/status/cultmesh/voidbot-swarm-state.cc`, written by
`scripts/render-voidbot-swarm-dashboard.mjs`.
- Idunn health witness:
`scripts/run-gamecult-orchestrator.ps1` publishes
`voidbot.cultnet-rudp-stack-health` over `cultnet.transport.rudp.v0` after
each orchestrator pulse. The health record reports the pulse summary for the
local stack; `scripts/check-voidbot-operations.ps1` remains a compatibility
probe and watchdog report, not the daemon liveness owner.
the Yggdrasil swarm publisher emits `voidbot.cultnet-rudp-stack-health` over
`cultnet.transport.rudp.v0`; Idunn consumes that provider-owned daemon health.
`scripts/check-voidbot-operations.ps1` is a manual/local diagnostic probe,
not a recurrence or daemon-liveness owner.
- JSON status packets under `.voidbot/status/` are debug/export surfaces unless
a specific runner owns them as command input. They are not durable canonical
state.
@ -98,7 +97,7 @@ typed CultCache documents behind the Eve binding:
channel permission projections, pending mention pressure, and compatibility
state access status.
- `voidbot.swarm`: CTB initiative order, active turns, pause/heat/cadence
controls, orchestrator organ health, and selected Face state witness.
controls, resident daemon health, and selected Face state witness.
## Eve Surfaces
@ -122,7 +121,7 @@ All meaningful presentation flows through Eve/CultUI DSL:
readiness.
- Swarm surface:
shows CTB order, active turn freeze, heat/cadence controls, pending mention
queues, orchestrator status, and selected Face state witness.
queues, daemon status, and selected Face state witness.
The existing `swarm-dashboard.html` file is a local browser lowering of the
`voidbot.swarm` Eve surface. It is useful for visual inspection, but it has no
@ -133,8 +132,9 @@ Eve binding and the underlying typed state owners.
1. Keep the current `voidbot.swarm` CultMesh publication healthy and discoverable
through Odin.
2. Keep `voidbot.cultnet-rudp-stack-health` publishing from the orchestrator
pulse so Idunn consumes daemon-owned health before compatibility probes.
2. Keep `voidbot.cultnet-rudp-stack-health` publishing from the resident
Yggdrasil transport boundary so Idunn consumes daemon-owned health without
local status scraping.
3. Add `.cc` witness/export documents for any remaining JSON-only command or
status surfaces before treating them as Verse state.
3. Move Persona and repo Face `.cc` inspection to Huginn-owned tooling while

View file

@ -20,7 +20,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
- Discord bot and worker are live.
- The unattended repo Face/native Persona CTB swarm runs on Yggdrasil. Resident `apps/persona-scheduler` inside `voidbot.service` owns the serialized Persona pulse and queues turns through Postgres for the worker. Void moderation, rumination, physiology, memory maintenance, and candidate delivery are now separate resident daemon organs; Persona initiative owns none of them. The Windows Local Orchestrator owns no Void decision loop.
- 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 Face/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 Face heartbeat runner skips and the orchestrator skips agent-swarm organs. This is the brake. Do not route around it.
- The repo-controlled pause flag is `state/agent-swarm-paused.json`. While it has `"paused": true` or is malformed, the resident Persona scheduler returns before registry, Discord, embedding, storage, or dispatch work. This is the brake. Do not route around it.
- Postgres owns jobs, audit events, interaction memory, and rate-limit state.
- Qdrant owns history and source vectors.
- `.voidbot/` owns archives, artifacts, logs, status files, and backups.
@ -36,9 +36,9 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
- Typed operations are applied through `packages/core/src/void-self-state-service.ts` and `scripts/void-self-state.mjs apply-operation`. The service now writes typed documents only; it no longer registers, reads, updates, or mirrors the old moderation monolith during routine operations.
- `scripts/export-recent-repo-activity.mjs` reads repo cursors from the typed `.cc` store. Legacy `.json` cursor arguments are only mapped to a sibling `.cc` path when needed by old invocation surfaces.
- The Windows rumination runner, installer, CTB moderation launcher, and PowerShell-only rumination/moderation fixtures are deleted. Their live invariants now belong to the resident TypeScript moderation, rumination, enforcement, and candidate-delivery organs and the consolidated initiative-engine smoke.
- 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.
- Prompt-facing moderation, rumination, and memory chronology is projected by the shared resident `relative-chronology-projector.ts`; exact timestamps remain typed state/cursor data.
- Resident physiology owns typed sleep/speaking pressure and emits maintenance intent. Resident memory maintenance consumes that intent once per nap, validates a whole operation batch, and verifies short-term residue is gone; the consolidated TypeScript smoke covers this without a local mood or memory runner.
- Persona scheduling is no longer part of the hidden Windows pulse. Yggdrasil runs `apps/persona-scheduler` as a resident compose service inside `voidbot.service`; it serializes CTB ticks and leaves execution to the worker. `GameCult Local Orchestrator` still describes legacy/local ownership for other organs and must not be treated as the Persona owner.
- Yggdrasil runs `apps/persona-scheduler` as a resident compose service inside `voidbot.service`; it serializes CTB ticks and leaves Persona execution to the worker. The Windows omnibus, installer, and hidden launcher are deleted.
- `scripts/start-voidbot-stack.ps1` now treats Bifrost's CultMesh command lane as a startup dependency. It reads `BIFROST_ROOT`, `BIFROST_CULTMESH_COMMAND_URI`, and `BIFROST_CULTMESH_STORE_PATH`, exports the provider store when missing, verifies the command processor exists, and records the result under `runtime-stack.json` as `bifrost`.
- r/GameCultOrg is the GameCult subreddit. Personas can now post there through
Bifrost and should be able to view threads when Bifrost provides that context.
@ -96,7 +96,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
- The repo-weather sermon machine got a leash too. The prompt now tells Void to lead with particulars from each repo before declaring a shared law; if the convergence only exists at slogan altitude, it should keep the reports split and spare the room the faux-cosmic glue.
- The moderation loop now treats direct asks to Void as persistent room obligations in typed `void.moderation_cursor.openCases`. A reviewed message is not automatically a handled message: if someone hands Void the floor, that obligation should survive cursor advancement and block optional repo-weather until Void actually answers or explicitly retires it.
- If quiet-room repo weather starts repeating itself, the intended correction is not a pile of extra cooldown law. The loop should treat that as a cue to go deeper into archive history, repo docs, diffs, or lore until it finds a better branch or honestly has nothing new to say.
- The mood drift organ is invoked by `GameCult Local Orchestrator`. It updates only typed scheduled-runtime sleep cycle and speaking pressure in `.voidbot/private/void-self-state.cc`, and invokes typed memory maintenance during sleep.
- Resident physiology updates typed sleep and speaking pressure in `.voidbot/private/void-self-state.cc` and emits a typed nap-maintenance intent for the separate resident memory organ.
- CultCache files use `.cc`. Do not document or create new self-state paths with `.msgpack`; that extension was an implementation-shaped lie.
- Repeated empty-room bookkeeping is low-signal. Refrains like `no new messages / no smoke / no post` should not become durable memory.
- Quiet-room exploration should now prefer underworked terrain when the usual seam is saturated. `bridge.source_coverage` and `bridge.refractory_topics` exist to push the next pass toward less-chewed repo families, archive years, or channels instead of rewarding repetition with another supposedly ripe synthesis.
@ -229,13 +229,13 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
- Yggdrasil rollout is verified at code revision `f23bfbd`: deployed initiative smoke passes; two consecutive resident pulses returned `agent_swarm_paused` with zero selected/queued turns; `docker top` showed only `node apps/persona-scheduler/dist/index.js` and no pulse child process. The one-minute observation loop is now actually cheap while paused, rather than merely avoiding model inference after paying process bootstrap.
- Pulse ownership is mapped in `notes/voidbot-daemon-organ-migration-map.md`. Void moderation, rumination, physiology, memory, and candidate delivery live in the Yggdrasil daemon; Bifrost dispatch belongs in Bifrost and operations supervision belongs in Idunn/ops. The Windows omnibus contains no fallback Void owner.
- Portable Void physiology now lives in `void-physiology-domain.ts` and `void-physiology-organ.ts`. The resident daemon invokes it on a five-minute cadence even while Persona inference is paused. It derives last speech from typed receipts, writes only typed sleep/speaking-pressure operations, freezes sleep transitions during active moderation, and emits sleep-memory pressure as an intent for the future memory organ. It never reads the old last-speech JSON cache or launches PowerShell/model maintenance.
- Yggdrasil physiology rollout is verified at `15a7c58`: the first pulse entered the due nap, updated typed speaking pressure, and emitted `sleep_short_term_pressure` for one memory; four subsequent one-minute observations did not rerun the five-minute organ; the container retained only its resident Node process. `void-mood-drift` has been deleted from `run-gamecult-orchestrator.ps1`, so re-enabling the disabled Windows omnibus cannot restore a competing mood writer.
- Yggdrasil physiology rollout is verified at `15a7c58`: the first pulse entered the due nap, updated typed speaking pressure, and emitted `sleep_short_term_pressure` for one memory; four subsequent one-minute observations did not rerun the five-minute organ. The local mood/memory bodies and Windows omnibus are now deleted, so no repository path can restore a competing writer.
- Direct Face `UPDATE REQUEST` and `BIFROST TOPIC` packets are ignored while `REPO_FACE_BIFROST_ENABLED=false`. Do not debug missing Bifrost jobs as a live failure unless that flag has been deliberately enabled again.
- Live model-facing prompt prose has started moving out of executable code. The shared loader is `packages/shared/src/prompt-template.ts`; extracted prompt surfaces live under `prompts/`, including repo Face turn/reviewer prompts, provider wrapper prompts, situational social read prompts, source-grounding reminders, consensus-feed prompts, and repo Face model-smoke scenarios. Code should project data and select prompt names; prompt files should own behavioral prose.
- Identity collapse has started. Active bot, worker, MCP repo-identity tools, and Face CTB scheduler paths now load through the canonical Face registry and treat repo Discord identities as compatibility projections. Next bounded move is to finish removing remaining direct conceptual dependence on repo identities, then split the Face turn loop into scheduler, context assembler, prompt renderer, candidate parser, policy gate, and transport adapter.
- Repo Face child prompts now load the full global Codex `AGENTS.md` as inherited doctrine near the top of each Face turn. This deliberately carries the Prime Directive, rebuild contract, humor doctrine, infrastructure habits, Epiphany stewardship, and Great Colossus mission frame into Face cognition rather than relying on a local summary shard.
- Repo Face child turns now preserve Discord media context. `SourceMessage` carries attachment metadata, heartbeat Discord fetch caches image attachments under `.voidbot/media/discord-images`, Face transcripts show `[media: image ... local=...]`, and owner Codex receives cached images through `codex exec --image`. Non-image attachments are visible as metadata only; Faces are explicitly told not to pretend they saw images unless a local image path was attached.
- After those gates exist, run dry/mock Face turns before re-enabling `GameCult Local Orchestrator`.
- After those gates exist, run dry/mock Face turns before deliberately unpausing the resident Persona scheduler.
- The direct-ask receipt check remains important, but it is no longer the next move while the swarm is offline.
- Next cuts should delete unstable foundations early: do not revive `legacyJsonPath`, working projections, top-level mirror fields, or cleanup code that only compensated for those broken boundaries.
- Keep sleep/distillation under watch. Fixture check: a memory like `AquariumSynthCSharp: Workflow cannot own the body` must survive sleep with its concrete subject, claim, anchor, tension, and implication intact.

View file

@ -131,10 +131,6 @@ VoidBot runs on Yggdrasil. Idunn owns deployment and consumes upstream repositor
- short-term clustering fixture. It proves repeated same-target/topic rumination proposals fold into one provisional memory with merged anchors, while a different topic in the same repo stays separate.
- `scripts/smoke-persona-initiative-engine.ts`
- consolidated resident-organ fixture. It proves moderation review/enforcement boundaries, rumination fingerprint and nap gates, typed advocacy accountability, candidate selection, shared Bifrost command receipts, and no-double-delivery without summoning a Windows runner.
- `scripts/run-gamecult-orchestrator.ps1`
- legacy local pulse for remaining Bifrost dispatch, swarm-surface projection, and operations-watchdog chores. It has no Persona, Void moderation, rumination, physiology, memory, or candidate-delivery authority.
- `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 Face Heartbeats`, `Void Mood Drift`, `Void Moderator Rumination`, and `VoidBot Operations Watchdog`.
- Live sleep maintenance
- passed once on real state after a manual typed nap. The maintenance runner consumed the scheduled rumination short-term memory with one `apply_memory_distillation` operation, leaving short-term memory empty and preserving one durable identity seam with target, claim, tension, action implication, and anchors.
- Sleep brake
@ -157,7 +153,7 @@ VoidBot runs on Yggdrasil. Idunn owns deployment and consumes upstream repositor
- Repo Face proposal/article PRs, PR comments, registered-identity Discord posts, and r/GameCultOrg Reddit thread posts now cross through parent-owned action blocks before Bifrost bridge commands. Bylined essays use the Interpreter-owned `ARTICLE` block: the Face writes naturally, the Interpreter extracts structured title/description/author/date/tags/path/body fields, and deterministic worker code renders/validates YAML frontmatter before calling `BIFROST_ROOT/tools/bifrost-bridge.mjs` for a draft PR and persona announcement. Subreddit threads use the Interpreter-owned `REDDIT THREAD` block: the Face writes a public title/body/share line, and deterministic worker code calls Bifrost `reddit-post` with Persona flair. The old `VOIDBOT_REPO_IDENTITY_ARTICLE` JSON sentinel is cut. Proposal PR and PR-comment sentinels still exist as compatibility rails. This is not merely helper placement: Bifrost's original governance/labor platform role makes it the owner of governed public crossings for GameCult work, including GitHub, r/GameCultOrg Reddit transport/thread access, Discord-native work/member/swarm interactions, dispatch receipts, CultNet/CultCache intake, and future collaboration interfaces. Heimdall is the OAuth/account/capability gate; Bifrost is the bridge/request/routing/receipt authority. VoidBot still owns room/thread cognition, moderation judgment, archive/source retrieval, and Face personality.
- Repo Face turns now receive a Bifrost governance digest from `BIFROST_ROOT/tools/governance-threads.mjs digest --repo <repo> --agent <identity>` alongside recent Discord context. Bifrost typed CultCache topics/comments are the canonical home for feature requests, Face opinions, missing questions, approvals, and dispatch receipts; Discord is a mirror/evidence source. When `BIFROST_DISCORD_CHANNEL_ID` is configured for `#bifrost`, repo Face Discord context ignores mirrored bot chatter there because the authoritative activity arrives through the Bifrost digest. Human `#bifrost` messages should only become governance comments after Bifrost/Heimdall can link the Discord id to a registered actor. Repo-local asks that would send a human or Codex to add/fix/name/scaffold/document/test/investigate/implement belong on Bifrost with enough room/repo context for a claimed Codex turn; a Face may still speak characterfully in Aquarium, but not as the only dispatch surface. Legacy `UPDATE REQUEST` packets are parsed only as compatibility input and reconciled into Bifrost topics instead of immediate transport enqueue. Local `#bifrost` is channel `1506760343836430376`; work proposals, governance mirrors, and Bifrost dispatch receipts should go there, while Aquarium remains the casual/social swarm room.
- Repo character turns now have a state-projector plus turn-Interpreter shape. The state-in boundary is `prompts/repo-face-state-projector.prompt.md`: typed state and identity material are translated into lived character prose before the child sees it. The state projector must surface values, drives, wounds, fascinations, aesthetic taste, dignity, mood, needs, social reads, bonds, agency pressure, and the personality-specific meaning of sparse or dense social topology when the state contains those facts. The deterministic state packet feeding that projector is intentionally verbose and parent-facing: it exposes private notes, values, activation vectors, runtime rest/speaking pressure, affect needs, bonds, status reads, mood dimensions, durable memories, short-term residue, agency pressures, incubating thoughts, candidate speech pressure, recent speech residue, social graph facts, room weather, and semantic curiosity graph attractors so every included variable can appear directly or bend behavior. The curiosity graph is built by seeding history/source vector retrieval from current room context, nearby channels, typed Face state, and home-jurisdiction identity, then decoding retrieved chunks into weighted attractor clusters with prominence, saturation, novelty, density, jurisdiction fit, suggested motion, and evidence labels. The projector owns turning those neutral facts into lived curiosity, boredom, fatigue, territorial pull, underexplored intrigue, or a closing/pivot impulse; deterministic code must not decide the feeling. Deterministic code may also attach neutral graph facts such as mapped people and unmapped registered peers to the projector packet, but it must not decide whether that feels like anxiety, territorial scouting, or nothing. Deterministic code attaches recent read-only home-repo activity before chat, using the Face's own state path as the repo-activity cursor source, so current implementation motion is visible as body context. Current-room and nearby-channel logs no longer cross a model conversation projector; deterministic code attaches raw oldest-to-newest transcript evidence with explicit recency/correction rules so the Face can notice human reframes directly. Deterministic code also attaches a social-affordance surface that matches active typed person-bonds/status reads to recent raw messages from that person, so relationship state arrives as a live hook rather than private decorative lore. Deterministic code still owns full prompt assembly and inspection through `scripts/run-repo-face-heartbeats.ts --assemble-prompt <identity>`; that path composes stable doctrine, allowed rooms, research/archive capability guidance, Bifrost digest, projected memory/affect prose, home-repo activity, live social affordances, and raw conversation evidence into the exact child prompt. It must not invent personality, mood, affect, or social memory from typed state. Situational obligations such as direct-call priority or first-public-introduction duty are emitted only when the scheduler packet proves they apply, not as standing prompt law. The child character writes a natural in-character turn on full `gpt-5.4` and may describe desired speech, repo/article proposals, social reads, needs, mood shifts, or memory-worthy thoughts without emitting action DSL blocks. The cheaper Spark/Mini parent Interpreter decides route/retry/drop and translates warranted effects into structured `STATE NOTE` and `SAY` blocks for the worker while governance dispatch remains paused. `STATE NOTE` blocks become typed short-term memory, affect needs, social bonds, status reads, mood dimensions, or agency pressure. The worker still accepts legacy compact JSON sentinels and legacy `UPDATE REQUEST` blocks for compatibility, but the live prompt path keeps transport and state syntax out of the character's roleplay surface.
- `apps/persona-scheduler` is the resident daemon organ that owns the serialized Persona wall-clock pulse on Yggdrasil. It invokes the remaining turn orchestrator without overlap and remains alive between pulses. `apps/persona-scheduler/src/initiative-engine.ts` owns the pure scheduling state machine: participant reconciliation, deterministic base speed/bias/threshold derivation, operator heat/speed projection, semantic-pressure commits, wall-clock advancement, active-turn freeze/completion recovery, queue-success/failure/dry-run commits, stale rescheduling, pending-mention priority, rest/idle eligibility, and ready ordering. Explicit blocked/withdrawn status, active load, semantic receipts, and initiative position survive reconciliation; new Faces without a usable mouth fail closed. `apps/persona-scheduler/src/state-store.ts` owns scheduler persistence and legacy migration. It stores one typed `voidbot.persona_scheduler_state` record in `repo-face-heartbeats.cc`, migrates the former JSON document once with a `storage_migrated` witness, rejects malformed CultCache instead of replacing it, and writes JSON only as a derived dashboard/debug projection. `packages/core/src/repo-face-heartbeat-queue.ts` resolves both legacy `.json` configuration and current `.cc` configuration to the same canonical path; the Discord bot publishes one immutable `voidbot.persona_attention_command` CultCache inbox document per addressed message. The scheduler store ingests and deduplicates those commands, persists them as pending attention, then acknowledges inbox files only after the durable commit; a crash before acknowledgement replays safely. `apps/persona-scheduler/src/active-turn-source.ts` owns the Postgres/legacy-lock liveness adapter. It recognizes only Persona turn request IDs, applies the bounded stale threshold, marks dead queue claims failed, and returns active/stale witnesses; it never writes scheduler state. `apps/persona-scheduler/src/persona-state-source.ts` owns read-only typed Persona-state acquisition and present-time physiology projection. It reports typed mind plus rest facts without creating missing state or writing sleep/speaking-pressure state; the initiative engine alone applies rest eligibility. `apps/persona-scheduler/src/persona-memory-context-source.ts` owns the derived vector refresh and semantic recall query for one supplied typed Mind observation. It returns typed hits/unavailability and cannot reacquire canonical state, render prompt prose, or queue work. `apps/persona-scheduler/src/persona-memory-projector.ts` owns deterministic/model projection policy, its prompt template, output sufficiency, leak rejection, all typed-state sections including clarity-conditioned agency/incubation/candidate/receipt pressure, and final ordering of named external fact sections. `apps/persona-scheduler/src/persona-social-context-projector.ts` owns time-explicit relationship freshness, room-texture observation, memory-facing room facts, turn-facing room weather, and pronoun-guidance rendering from supplied facts. Neither projector may acquire Discord, reopen Persona state, retrieve vectors, or queue work. `apps/persona-scheduler/src/discord-activity-source.ts` owns neutral Discord activity acquisition: it deduplicates explicit watched channels, excludes bot traffic, reports raw human messages, and derives idle-cooling times from scheduler history without writing state or selecting work. `apps/persona-scheduler/src/semantic-pressure-source.ts` owns embedding-backed affinity observation and returns bounded projections for eligible Personas and permitted channels. It cannot mutate initiative; `initiative-engine.ts` alone applies pressure, dynamic heat, interrupt readiness, dedupe receipts, and transition history. `apps/persona-scheduler/src/turn-context-source.ts` owns current-room and nearby-channel Discord evidence plus the bounded image-attachment cache. It returns chronological source messages and explicit snapshot failure witnesses; it has no access to Persona state, prompt composition, or the job queue. `scripts/run-repo-face-heartbeats.ts` projects the identity registry into neutral participant specs and affinity candidates, still computes social-graph/peer-opening/relationship-pressure/human-clarity/maintenance facts, passes all facts by name to the projector, and invokes external queue actuators pending further extraction. External adapters report facts; only the initiative engine mutates load, counters, recovery position, pressure, and queue-transition history. Repo Faces and native Personas queue ordinary `repo-face-rumination` jobs through the canonical job queue. Built-in Void participates only when its separate moderation heartbeat organ is enabled; the hosted scheduler does not launch Windows PowerShell.
- `apps/persona-scheduler` is the resident daemon organ that owns the serialized Persona wall-clock pulse on Yggdrasil and remains alive between observations. It queues only registered repo/native Personas; built-in Void never enters initiative. `initiative-engine.ts` owns scheduling state transitions, `state-store.ts` owns typed persistence/migration, attention commands are acknowledged only after durable commit, and external sources/actuators return facts without writing initiative.
- Human-clarity chronology, social topology, peer openings, and relationship-pressure scoring now share the same social-context owner. The shell consumes one named projection result and contains no duplicate social scorer.
- Self-maintenance advocacy is a typed-state pressure projection owned by `persona-memory-projector.ts`. That organ classifies qualifying needs, agency pressures, and must-share candidates, suppresses them under live human-clarity pressure, and fixes their order alongside the other pressure sections; the heartbeat shell no longer carries a parallel maintenance policy.
- Curiosity is split at the evidence boundary. `persona-curiosity-context-source.ts` plans semantic seeds from supplied identity, typed Mind, and room evidence, then acquires and deduplicates bounded neutral nodes through an injected retrieval port. `persona-curiosity-projector.ts` constructs and scores the local attractor graph and renders curiosity weather without vector access. `persona-curiosity-terms.ts` owns their shared topic-term vocabulary. The heartbeat shell wires the existing RAG adapter into these organs and owns neither query policy nor graph interpretation.
@ -177,8 +173,8 @@ VoidBot runs on Yggdrasil. Idunn owns deployment and consumes upstream repositor
- `apps/persona-scheduler/src/repo-activity-source.ts` owns the home-repo activity subprocess boundary. It resolves the source repo and Face cursor, invokes `export-recent-repo-activity.mjs` with a bounded 96-hour/five-commit window and mandatory `--read-only`, and returns a typed observation. Prompt composition renders that observation and cannot invoke or parse the exporter.
- Idunn invokes the root-owned audited manifest at `/srv/odin/deploy-manifests/voidbot`; `/usr/local/sbin/deploy-voidbot` is a byte-identical inspection/recovery copy, not the live path. Both derive from `gamecult-ops/scripts/deploy-voidbot.sh`. The `idunn` user exclusively owns mutable Git state in `/srv/build/VoidBot` and `/srv/build/cultcache-ts`, including lazy partial-clone object fetches; the root actuator owns immutable artifact construction/promotion, systemd, and deployment witnesses without running Git as root.
- The same scheduler derives semantic `responsePressure` for scheduler-eligible Personas by embedding canonical Face identity-card projections and recent human messages from channels each Face may enter. Pressure decays by message age, aggregates with noisy-OR, awakens at most three eligible Faces, and exposes its message-scoped cosine evidence. It produces bounded `dynamicHeat` for the next scheduled recovery calculation; an unseen message crossing the Persona's interrupt threshold may pull readiness to the current initiative clock exactly once. It cannot create a job, bypass rest or active-turn brakes, impersonate a direct mention, or write inferred desire into Persona state.
- The Windows `GameCult Local Orchestrator` and per-organ scheduled-task wrapper no longer own Persona pulses. `voidbot.service` deploys `persona-scheduler` beside bot, worker, Qdrant, and the CultMesh publisher. Turn frequency is controlled by `REPO_FACE_HEARTBEAT_BASE_RECOVERY_MINUTES`, `REPO_FACE_HEARTBEAT_GLOBAL_HEAT`, `REPO_FACE_HEARTBEAT_SPEED_OVERRIDES`, and `REPO_FACE_HEARTBEAT_HEAT_OVERRIDES`. Heat keys can target `all`, `kind:<participantKind>`, `turn:<turnKind>`, `identity:<id>`, `repo:<repo>`, `display:<name>`, `channel:<id>`, or the bare identity/repo/display key.
- The local ignored registry currently includes Nibu for `AetheriaLore`, with Face state in `E:/Projects/AetheriaLore/.voidbot/state/nibu.cc`, Discord role id `1505918221453103247`, and an Aquarium channel allow-list. Nibu's Face state now treats AetheriaLore-first map-building, abrasive character voice, consensus-backed change agency, her own `Aetheria/Lore/Nibu.md` character-note stewardship, Nibu-authored lore essays, and tangential setting fascination as separate active doctrines. That tangential fascination points at embodied ship minds, junkyard abandonment, murderous autonomy, salvage, and save-scumming survival. The repo Face heartbeat organ is now pulsed by `GameCult Local Orchestrator`; after restarting the stack onto the new worker code, forced heartbeat job `76020f1e-0c55-424b-9e35-89ba7f77fcc3` completed without base-bot auto-posting.
- The Windows omnibus and per-organ wrappers are deleted. `voidbot.service` deploys `persona-scheduler` beside bot, worker, Qdrant, and the CultMesh publisher. Heat changes Persona recovery only and cannot manufacture intent.
- The registry includes Nibu for `AetheriaLore`, with typed Face state and an Aquarium channel allow-list. Her Persona turns use the resident scheduler and normal worker queue; no local orchestrator exists.
- The local ignored registry now also includes Epiphany for `EpiphanyAgent`, with Face state in `E:/Projects/EpiphanyAgent/.voidbot/state/epiphany.cc`, Discord role id `1506375937439301642`, and Aquarium channel allow-list `1501196543150264332`. Her repo-local `.voidbot` birth surface has completed in plan/review mode; the generated EpiphanyAgent startup packets still require Self review before accepted native birth mutations. Her jurisdiction is the self-improvement spine: typed state, CultCache/CultNet, Codex bridge boundaries, birth/init rites, low-copy runtime contracts, review gates, graph-shaped memory, heartbeat physiology, and deletion of duplicated authority.
- `scripts/export-random-discord-history.mjs`
- plain Node helper for novelty excursions that samples one random archived Discord seam, returns a local context window around the anchor message, and skips bot-directed prompt rows by default.
@ -204,7 +200,7 @@ When present, an ignored local script at `.voidbot/private/check-voidbot-operati
- Odin owns Verse/provider discovery and interface aggregation, not provider state.
- Eve/CultUI owns presentation shape; browser/native/TUI renderers lower provider-owned surfaces.
- Inputs:
- Discord gateway events, archived Discord corpus, indexed source/lore archives, Postgres jobs/audit/interaction memory, Qdrant vectors, typed `.cc` self/Face state, Bifrost governance digest, and orchestrator status.
- Discord gateway events, archived Discord corpus, indexed source/lore archives, Postgres jobs/audit/interaction memory, Qdrant vectors, typed `.cc` self/Face state, Bifrost governance digest, and resident daemon health.
- Outputs:
- Discord replies or webhook persona posts through parent-owned rails, owner handoff artifacts, MCP retrieval/tool results, typed CultCache operations, and CultMesh-advertised Eve/CultUI provider surfaces.
- Derived state:
@ -242,7 +238,7 @@ Target Eve surfaces:
- `voidbot.archive`: archived Discord corpus status, import/backfill health, bot-directed-prompt exclusion, history vector freshness.
- `voidbot.source`: repo/lore shard freshness, indexed coverage, Qdrant collection health, detached reindex jobs.
- `voidbot.repo_face`: registered identities, repo-local `.cc` witnesses, channel grants, prompt assembly status, Bifrost digest availability, Huginn inspection readiness.
- `voidbot.swarm`: CTB order, active turns, pending mention queues, heat/cadence controls, orchestrator status, selected Face state witness.
- `voidbot.swarm`: CTB order, active turns, pending mention queues, heat/cadence controls, daemon status, selected Face state witness.
Migration order:

View file

@ -4,7 +4,7 @@ Observed 2026-07-16 after the Yggdrasil replacement. This map distinguishes live
## Live Bodies
- Starfire `GameCult Local Orchestrator`: disabled.
- Starfire `GameCult Local Orchestrator`: deleted from the repository; the previously installed task remains operational debris to remove if still present on Starfire.
- Starfire `Void Moderator Rumination`: disabled.
- Starfire `Void Mood Drift`: disabled.
- Starfire `VoidBot Operations Watchdog`: disabled.
@ -50,4 +50,4 @@ The disabled tasks are not standby owners. The listed organs currently have no r
## Deletion Line
After each target organ is live and verified on Yggdrasil, remove its definition from `run-gamecult-orchestrator.ps1` and stop watchdog logic from treating the disabled Local Orchestrator as a valid owner. The rebuild is incomplete while an old Windows path can still be re-enabled and produce the same decision independently.
The Windows omnibus, installer, and hidden launcher are deleted, and watchdog logic cannot recognize them as health owners. Remaining per-organ Starfire tasks are operational debris, not standby paths; remove any installed copies rather than restoring repository launchers.

View file

@ -214,7 +214,7 @@ The canonical teardown map is `notes/agent-swarm-teardown-plan-2026-05-21.md`.
First cuts:
- Keep `GameCult Local Orchestrator` disabled while swarm teardown proceeds.
- The Windows omnibus is deleted. Keep the resident Persona scheduler paused while swarm teardown proceeds.
- Landed: add a repo-controlled pause gate that swarm organs obey even when the Windows scheduled task or manual runner is invoked.
- Landed: rename child-facing `heartbeat` framing to neutral turn/pass language so runtime provenance stops poisoning public speech.
- Landed: add parent-side public speech/work eligibility before transport as a Spark/Codex reviewer pass with one retry. The reviewer gives concrete reasons; the Face gets one revision; the parent routes or drops the final output.
@ -275,12 +275,12 @@ Restart gates:
- Landed in the active path: routine self-state operations use `.voidbot/private/void-self-state.cc`, and the typed service no longer registers or mirrors the legacy moderation document.
- `scripts/run-void-moderator-rumination.ps1` restores rumination through `prompts/void-moderator-rumination.md` and typed operation output; it does not materialize `.json`.
- `scripts/simulate-void-mood.mjs` is typed-only sleep/speaking pressure and does not refresh a JSON projection.
- Resident `void-physiology-organ.ts` owns typed sleep/speaking pressure and emits maintenance intent without JSON projection.
- Remaining work: keep any explicit human debug export separate from mutation authority, and delete stale references/invocations as they are found.
### Commit 6.5: Keep Old Scripts Offline
- `Void Mood Drift` has been deliberately reinstalled after the typed sleep-maintenance fixture passed. It runs `scripts/run-void-mood-drift.ps1`, which calls the typed `scripts/simulate-void-mood.mjs` path.
- Resident physiology and memory maintenance replace the deleted mood task, wrapper, simulator, and PowerShell maintenance runner.
- `Void Moderator Rumination` is re-enabled after typed operation quality, parent-owned speech closure, and an observed live scheduled pass. The runner has model-branch, parent-owned speech, and nap-skip fixtures.
- Do not reinstall or re-enable deleted legacy scripts as compatibility surfaces.
- Verification: scheduled-task query shows mood and moderation enabled with Last Result `0`, and rebuilt direct dry runs operate only on `.voidbot/private/void-self-state.cc`.
@ -308,8 +308,8 @@ Restart gates:
- Landed cut: cleanup paths whose only job was repairing JSON projection edits, legacy mirrors, keyword sludge, or overgrown runtime residue have been deleted with the old memory-organ stack.
- Landed cut: hard-wired agency paths that turned heuristic scores into doctrine, advocacy, or speech candidates without an explicit typed-state contract have been deleted.
- Landed replacement boundary: `scripts/run-void-memory-maintenance.ps1` asks the model for memory/incubation/candidate typed operation proposals using `prompts/void-memory-maintenance.md`, rejects non-maintenance operations, and applies the rest through `scripts/void-self-state.mjs`.
- Landed sleep integration: `scripts/simulate-void-mood.mjs` invokes memory maintenance once per nap, and the maintenance runner fails a real sleep pass that returns no operations while memory pressure is present.
- Resident memory maintenance asks one tool-free model call for a fully validated typed operation batch, pre-receipts one attempt per nap, and fails if short-term residue survives.
- Resident physiology emits the nap intent; no mood script, PowerShell runner, or filesystem lock participates.
- Landed verification: `npm run moderation:memory-maintenance:sleep-fixture` exercises the non-skip model branch with a fake Codex child, proving `AquariumSynthCSharp: Workflow cannot own the body` promotes from short-term into durable memory while preserving subject, claim, anchor, tension, and action implication.
- Landed memory lifecycle: `revise_durable_memory`, `retire_durable_memory`, and `crystallize_memory_into_identity` make long-term memory durable but plastic instead of silently immutable.
- Landed lifecycle verification: `npm run moderation:memory-lifecycle:fixture` proves a short-term thought can promote into durable memory, revise into a replacement, retire superseded durable records, and crystallize into an identity memory plus self-profile value.

View file

@ -1264,21 +1264,11 @@ try {
try {
$watchdogTask = Get-ScheduledTask -TaskName $watchdogTaskName -ErrorAction Stop
$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" }
$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."
} else {
"Operations watchdog task is installed but disabled, and GameCult Local Orchestrator is not enabled."
}
$watchdogTaskStatus = if ($watchdogEnabled) { "passed" } else { "failed" }
$watchdogTaskDetail = if ($watchdogEnabled) { "Operations watchdog task is installed and enabled." } else { "Operations watchdog task is installed but disabled." }
Add-Check -Name "scheduled_task.watchdog" -Status $watchdogTaskStatus -Detail $watchdogTaskDetail -Data @{
taskName = $watchdogTaskName
state = [string]$watchdogTask.State
orchestratorTaskName = "GameCult Local Orchestrator"
orchestratorEnabled = $orchestratorEnabled
}
} catch {
Add-Check -Name "scheduled_task.watchdog" -Status "warning" -Detail "Operations watchdog task $watchdogTaskName is not installed yet."

View file

@ -168,7 +168,7 @@ function buildCatalog() {
provider({
id: "voidbot.swarm",
title: "VoidBot Swarm",
description: "CTB initiative order, active turns, pause/heat/cadence controls, orchestrator organ health, and selected Face state witness.",
description: "CTB initiative order, active turns, pause/heat/cadence controls, daemon health, and selected Face state witness.",
status: "live",
surfaces: [
surface("voidbot.swarm.ctb_order", "CTB order", "Initiative order, active turns, recovery timing, pending mentions, and selected Face witness."),
@ -178,7 +178,6 @@ function buildCatalog() {
witness("voidbot.swarm_state_snapshot", ".voidbot/status/cultmesh/voidbot-swarm-state.cc", "scripts/render-voidbot-swarm-dashboard.mjs", ["voidbot.swarm_state_snapshot", "gamecult.eve.provider_advertisement", "gamecult.eve.surface_state", "gamecult.eve.interface_binding"]),
witness("voidbot.repo_face_heartbeat_state", ".voidbot/status/repo-face-heartbeats.cc", "VoidBot CTB scheduler", ["canonical typed scheduler state"]),
witness("voidbot.repo_face_heartbeat_debug_projection", ".voidbot/status/repo-face-heartbeats.json", "VoidBot CTB scheduler", ["derived debug/status projection", "not mutation authority"]),
witness("voidbot.orchestrator_state", ".voidbot/status/gamecult-orchestrator.json", "GameCult Local Orchestrator", ["debug/status source"]),
],
commands: [
command("swarm.inspect", "read-only", "Load the current CTB and selected Face state surface."),

View file

@ -1,66 +0,0 @@
param(
[string] $TaskName = "GameCult Local Orchestrator",
[int] $IntervalMinutes = 1,
[switch] $DisableLegacyTasks,
[switch] $RunNow
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$repoRoot = Split-Path -Parent $PSScriptRoot
$runnerScript = Join-Path $PSScriptRoot "run-gamecult-orchestrator.ps1"
$hiddenLauncher = Join-Path $PSScriptRoot "start-gamecult-orchestrator-hidden.vbs"
if ($IntervalMinutes -lt 1) {
throw "IntervalMinutes must be at least 1."
}
if (-not (Test-Path -LiteralPath $runnerScript)) {
throw "Missing orchestrator script at $runnerScript."
}
if (-not (Test-Path -LiteralPath $hiddenLauncher)) {
throw "Missing hidden PowerShell launcher at $hiddenLauncher."
}
$startAt = (Get-Date).AddMinutes(1)
$action = New-ScheduledTaskAction `
-Execute "wscript.exe" `
-Argument "//B //nologo `"$hiddenLauncher`"" `
-WorkingDirectory $repoRoot
$trigger = New-ScheduledTaskTrigger -Once -At $startAt -RepetitionInterval (New-TimeSpan -Minutes $IntervalMinutes) -RepetitionDuration (New-TimeSpan -Days 3650)
$settings = New-ScheduledTaskSettingsSet -StartWhenAvailable -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Minutes 30) -MultipleInstances IgnoreNew
$principal = New-ScheduledTaskPrincipal -UserId "$env:USERDOMAIN\$env:USERNAME" -LogonType Interactive -RunLevel Limited
$description = "Runs the local GameCult agent pulse scheduler and records per-organ status."
Register-ScheduledTask -TaskName $TaskName -Action $action -Trigger $trigger -Settings $settings -Principal $principal -Description $description -Force | Out-Null
Write-Host "Installed scheduled task: $TaskName"
Write-Host "Schedule: every $IntervalMinutes minute(s), starting at $($startAt.ToString('yyyy-MM-dd HH:mm'))"
Write-Host "Working directory: $repoRoot"
Write-Host "Runner script: $runnerScript"
if ($DisableLegacyTasks) {
$legacyTasks = @(
"Bifrost Agent Dispatch",
"VoidBot Repo Face Heartbeats",
"Void Mood Drift",
"Void Moderator Rumination",
"VoidBot Operations Watchdog"
)
foreach ($legacyTask in $legacyTasks) {
$task = Get-ScheduledTask -TaskName $legacyTask -ErrorAction SilentlyContinue
if ($null -eq $task) {
Write-Host "Legacy task not found: $legacyTask"
continue
}
Disable-ScheduledTask -TaskName $legacyTask | Out-Null
Write-Host "Disabled legacy task: $legacyTask"
}
}
if ($RunNow) {
Start-ScheduledTask -TaskName $TaskName
Write-Host "Started task immediately."
}

View file

@ -1,592 +0,0 @@
param(
[string[]] $Only,
[switch] $Force
)
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$ProgressPreference = "SilentlyContinue"
$repoRoot = Split-Path -Parent $PSScriptRoot
$statusDir = Join-Path $repoRoot ".voidbot\status"
$logDir = Join-Path $repoRoot ".voidbot\logs\orchestrator"
$statePath = Join-Path $statusDir "gamecult-orchestrator.json"
$lockPath = Join-Path $statusDir "gamecult-orchestrator.lock"
$agentSwarmPausePath = Join-Path $repoRoot "state\agent-swarm-paused.json"
$hiddenLauncher = Join-Path $PSScriptRoot "run-hidden-powershell.vbs"
$bifrostRoot = "E:\Projects\Bifrost"
$bootstrapLogPath = Join-Path $logDir "bootstrap.log"
function Write-BootstrapLog {
param([string] $Message)
try {
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
$line = "[{0}] pid={1} cwd={2} {3}" -f ([DateTime]::UtcNow.ToString("o")), $PID, (Get-Location).Path, $Message
Add-Content -LiteralPath $bootstrapLogPath -Encoding UTF8 -Value $line
} catch {
}
}
function Read-DotEnv {
param([Parameter(Mandatory = $true)][string] $Path)
$values = @{}
if (-not (Test-Path -LiteralPath $Path)) {
return $values
}
foreach ($line in Get-Content -LiteralPath $Path -Encoding UTF8) {
$trimmed = $line.Trim()
if ($trimmed.Length -eq 0 -or $trimmed.StartsWith("#")) {
continue
}
$separator = $trimmed.IndexOf("=")
if ($separator -lt 1) {
continue
}
$values[$trimmed.Substring(0, $separator).Trim()] = $trimmed.Substring($separator + 1).Trim()
}
return $values
}
function Get-ConfigInt {
param(
[hashtable] $Config,
[string] $Name,
[int] $Default,
[int] $Minimum
)
$raw = [Environment]::GetEnvironmentVariable($Name)
if ([string]::IsNullOrWhiteSpace($raw) -and $Config.ContainsKey($Name)) {
$raw = $Config[$Name]
}
$value = $Default
if (-not [string]::IsNullOrWhiteSpace($raw)) {
$value = [int]$raw
}
return [Math]::Max($Minimum, $value)
}
function Get-ConfigBool {
param(
[hashtable] $Config,
[string] $Name,
[bool] $Default
)
$raw = [Environment]::GetEnvironmentVariable($Name)
if ([string]::IsNullOrWhiteSpace($raw) -and $Config.ContainsKey($Name)) {
$raw = $Config[$Name]
}
if ([string]::IsNullOrWhiteSpace($raw)) {
return $Default
}
return @("1", "true", "yes", "on").Contains($raw.Trim().ToLowerInvariant())
}
function Read-JsonFile {
param([string] $Path)
if (-not (Test-Path -LiteralPath $Path)) {
return $null
}
$raw = Get-Content -LiteralPath $Path -Raw -Encoding UTF8
if ([string]::IsNullOrWhiteSpace($raw)) {
return $null
}
return $raw | ConvertFrom-Json
}
function Write-JsonFile {
param([string] $Path, $Data)
$directory = Split-Path -Parent $Path
New-Item -ItemType Directory -Force -Path $directory | Out-Null
$json = $Data | ConvertTo-Json -Depth 32
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
[System.IO.File]::WriteAllText($Path, $json, $utf8NoBom)
}
function ConvertTo-WindowsArgument {
param([string] $Value)
if ($Value -notmatch '[\s"]') {
return $Value
}
return '"' + ($Value -replace '(\\*)"', '$1$1\"' -replace '(\\+)$', '$1$1') + '"'
}
function Join-WindowsArguments {
param([object[]] $Arguments)
return (($Arguments | ForEach-Object { ConvertTo-WindowsArgument -Value ([string]$_) }) -join " ")
}
function Publish-IdunnRudpHealth {
param(
[Parameter(Mandatory = $true)] $Summary
)
$publisher = Join-Path $PSScriptRoot "publish-idunn-rudp-health.cjs"
if (-not (Test-Path -LiteralPath $publisher)) {
return
}
$endpoint = $env:VOIDBOT_IDUNN_RUDP_HEALTH
if ([string]::IsNullOrWhiteSpace($endpoint)) {
Write-BootstrapLog "skipping-idunn-health VOIDBOT_IDUNN_RUDP_HEALTH is not configured"
return
}
$state = if ($Summary.ok) { "healthy" } else { "failed" }
$detail = "VoidBot orchestrator pulse ran $($Summary.ranCount) organ(s) in $($Summary.durationSeconds)s; ok=$($Summary.ok)"
try {
Write-BootstrapLog "publishing-idunn-health state=$state ran=$($Summary.ranCount) duration=$($Summary.durationSeconds)"
$nodeExe = (Get-Command node.exe -ErrorAction Stop).Source
$output = & $nodeExe $publisher `
--endpoint $endpoint `
--daemon "voidbot" `
--contract "voidbot.cultnet-rudp-stack-health" `
--state $state `
--detail $detail `
--observed-at $Summary.finishedAt 2>&1
if ($LASTEXITCODE -ne 0) {
throw "publisher exited with code $LASTEXITCODE`: $($output -join [Environment]::NewLine)"
}
Write-BootstrapLog "published-idunn-health"
} catch {
$message = "[$([DateTime]::UtcNow.ToString("o"))] Idunn RUDP health publish failed: $($_.Exception.Message)"
Add-Content -LiteralPath (Join-Path $logDir "idunn-rudp-health.log") -Encoding UTF8 -Value $message
Write-BootstrapLog "publish-idunn-health-failed $($_.Exception.Message)"
}
}
function Get-ConfigString {
param(
[hashtable] $Config,
[string] $Name,
[string] $Default
)
$raw = [Environment]::GetEnvironmentVariable($Name)
if ([string]::IsNullOrWhiteSpace($raw) -and $Config.ContainsKey($Name)) {
$raw = $Config[$Name]
}
if ([string]::IsNullOrWhiteSpace($raw)) {
return $Default
}
return $raw.Trim()
}
function Find-VoidBotSwarmCultMeshProcess {
param([string] $ScriptPath)
$escaped = $ScriptPath.Replace("\", "\\")
return @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
$_.Name -eq "node.exe" -and
$_.CommandLine -and
($_.CommandLine -like "*serve-voidbot-swarm-cultmesh.cjs*" -or $_.CommandLine -like "*$escaped*")
})
}
function Ensure-VoidBotSwarmCultMeshServer {
param(
[hashtable] $Config,
[string] $NodePath
)
$scriptPath = Join-Path $PSScriptRoot "serve-voidbot-swarm-cultmesh.cjs"
if (-not (Test-Path -LiteralPath $scriptPath)) {
throw "VoidBot swarm CultMesh server script is missing at $scriptPath."
}
$storePath = Get-ConfigString -Config $Config -Name "VOIDBOT_SWARM_CULTMESH_STORE" -Default (Join-Path $statusDir "cultmesh\voidbot-swarm-state.cc")
$storePath = [System.IO.Path]::GetFullPath($storePath)
if (-not (Test-Path -LiteralPath $storePath)) {
throw "VoidBot swarm CultMesh store is missing at $storePath."
}
$bind = Get-ConfigString -Config $Config -Name "VOIDBOT_SWARM_CULTMESH_BIND" -Default "127.0.0.1:17873"
$odinCultMeshUri = Get-ConfigString -Config $Config -Name "VOIDBOT_ODIN_CULTMESH_URI" -Default (
Get-ConfigString -Config $Config -Name "ODIN_CULTMESH_URI" -Default "cultmesh://odin/rendezvous/provider-catalog"
)
$odinRudpEndpoint = Get-ConfigString -Config $Config -Name "VOIDBOT_ODIN_RUDP" -Default (
Get-ConfigString -Config $Config -Name "CULTMESH_URI_ODIN_RUDP" -Default "rudp://127.0.0.1:17871"
)
$existing = @(Find-VoidBotSwarmCultMeshProcess -ScriptPath $scriptPath)
if ($existing.Count -gt 0) {
return [pscustomobject]@{
status = "already_running"
pid = [int]$existing[0].ProcessId
bind = $bind
storePath = $storePath
odinCultMeshUri = $odinCultMeshUri
odinRudpEndpoint = $odinRudpEndpoint
}
}
$stdoutPath = Join-Path $logDir "voidbot-swarm-cultmesh.stdout.log"
$stderrPath = Join-Path $logDir "voidbot-swarm-cultmesh.stderr.log"
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
$arguments = @(
$scriptPath,
"--store", $storePath,
"--bind", $bind,
"--odin-cultmesh-uri", $odinCultMeshUri,
"--odin-rudp-endpoint", $odinRudpEndpoint
)
$process = Start-Process -FilePath $NodePath -ArgumentList $arguments -WorkingDirectory $repoRoot -RedirectStandardOutput $stdoutPath -RedirectStandardError $stderrPath -WindowStyle Hidden -PassThru
Start-Sleep -Seconds 2
$process.Refresh()
if ($process.HasExited) {
$stdoutTail = if (Test-Path -LiteralPath $stdoutPath) { (Get-Content -LiteralPath $stdoutPath -Tail 40) -join "`n" } else { "" }
$stderrTail = if (Test-Path -LiteralPath $stderrPath) { (Get-Content -LiteralPath $stderrPath -Tail 40) -join "`n" } else { "" }
throw "VoidBot swarm CultMesh server exited early.`nSTDOUT:`n$stdoutTail`nSTDERR:`n$stderrTail"
}
return [pscustomobject]@{
status = "started"
pid = $process.Id
bind = $bind
storePath = $storePath
odinCultMeshUri = $odinCultMeshUri
odinRudpEndpoint = $odinRudpEndpoint
stdoutPath = $stdoutPath
stderrPath = $stderrPath
}
}
function Get-OrganState {
param($State, [string] $Id)
$property = $State.organs.PSObject.Properties[$Id]
if ($null -eq $property) {
return $null
}
return $property.Value
}
function Set-OrganState {
param($State, [string] $Id, $Value)
$State.organs | Add-Member -NotePropertyName $Id -NotePropertyValue $Value -Force
}
function Set-SkippedOrganState {
param($State, $Organ, [datetime] $Now, [string] $Status, $LogPath = $null)
Set-OrganState -State $State -Id $Organ.Id -Value ([pscustomobject]@{
label = $Organ.Label
intervalMinutes = $Organ.IntervalMinutes
lastStartedAt = $Now.ToString("o")
lastFinishedAt = $Now.ToString("o")
lastExitCode = 0
lastStatus = $Status
lastLogPath = $LogPath
})
}
function Test-Due {
param($State, [string] $Id, [int] $IntervalMinutes, [datetime] $Now)
if ($Force) {
return $true
}
$organState = Get-OrganState -State $State -Id $Id
if ($null -eq $organState -or [string]::IsNullOrWhiteSpace($organState.lastStartedAt)) {
return $true
}
$lastStartedAt = [datetime]::Parse($organState.lastStartedAt).ToUniversalTime()
return ($Now - $lastStartedAt).TotalMinutes -ge $IntervalMinutes
}
function Invoke-Organ {
param($Organ, [datetime] $Now)
$runId = $Now.ToString("yyyyMMdd-HHmmss") + "-" + $Organ.Id
$logPath = Join-Path $logDir "$runId.log"
$stdoutPath = Join-Path $logDir "$runId.stdout.log"
$stderrPath = Join-Path $logDir "$runId.stderr.log"
New-Item -ItemType Directory -Force -Path $logDir | Out-Null
$startedAt = [DateTime]::UtcNow
Write-BootstrapLog "invoke-organ start id=$($Organ.Id) executable=$($Organ.Executable)"
$exitCode = 0
$timedOut = $false
try {
Push-Location -LiteralPath $Organ.Cwd
$startInfo = [System.Diagnostics.ProcessStartInfo]::new()
$startInfo.FileName = $Organ.Executable
$startInfo.Arguments = Join-WindowsArguments -Arguments $Organ.Arguments
$startInfo.WorkingDirectory = $Organ.Cwd
$startInfo.UseShellExecute = $false
$startInfo.CreateNoWindow = $true
$startInfo.RedirectStandardOutput = $true
$startInfo.RedirectStandardError = $true
$process = [System.Diagnostics.Process]::new()
$process.StartInfo = $startInfo
[void]$process.Start()
$stdoutTask = $process.StandardOutput.ReadToEndAsync()
$stderrTask = $process.StandardError.ReadToEndAsync()
$timeoutSeconds = [Math]::Max(60, $Organ.TimeoutMinutes * 60)
if (-not $process.WaitForExit($timeoutSeconds * 1000)) {
$timedOut = $true
cmd /c "taskkill /PID $($process.Id) /T /F" | Out-Null
$process.WaitForExit()
}
$stdout = $stdoutTask.GetAwaiter().GetResult()
$stderr = $stderrTask.GetAwaiter().GetResult()
[System.IO.File]::WriteAllText($stdoutPath, $stdout, [System.Text.UTF8Encoding]::new($false))
[System.IO.File]::WriteAllText($stderrPath, $stderr, [System.Text.UTF8Encoding]::new($false))
$exitCode = if ($timedOut) { 124 } elseif ($null -eq $process.ExitCode) { 0 } else { $process.ExitCode }
} catch {
Set-Content -LiteralPath $stderrPath -Encoding UTF8 -Value ($_ | Out-String)
$exitCode = 1
} finally {
Pop-Location
}
$finishedAt = [DateTime]::UtcNow
$stdout = ""
if (Test-Path -LiteralPath $stdoutPath) {
$stdout = [string](Get-Content -LiteralPath $stdoutPath -Raw -Encoding UTF8)
}
if ($null -eq $stdout) {
$stdout = ""
}
$stderr = ""
if (Test-Path -LiteralPath $stderrPath) {
$stderr = [string](Get-Content -LiteralPath $stderrPath -Raw -Encoding UTF8)
}
if ($null -eq $stderr) {
$stderr = ""
}
$combined = @(
"exitCode=$exitCode"
"timedOut=$timedOut"
"stdout:"
$stdout.Trim()
"stderr:"
$stderr.Trim()
) -join [Environment]::NewLine
[System.IO.File]::WriteAllText($logPath, $combined.Trim(), [System.Text.UTF8Encoding]::new($false))
Write-BootstrapLog "invoke-organ finish id=$($Organ.Id) status=$(if ($timedOut) { 'timed_out' } elseif ($exitCode -eq 0) { 'ok' } else { 'failed' }) exit=$exitCode"
return [pscustomobject]@{
id = $Organ.Id
label = $Organ.Label
startedAt = $startedAt.ToString("o")
finishedAt = $finishedAt.ToString("o")
durationSeconds = [Math]::Round(($finishedAt - $startedAt).TotalSeconds, 2)
exitCode = $exitCode
status = if ($timedOut) { "timed_out" } elseif ($exitCode -eq 0) { "ok" } else { "failed" }
logPath = $logPath
}
}
function Test-OrchestratorLock {
if (-not (Test-Path -LiteralPath $lockPath)) {
return $false
}
$lock = Read-JsonFile -Path $lockPath
if ($null -eq $lock -or [string]::IsNullOrWhiteSpace($lock.startedAt)) {
Remove-Item -LiteralPath $lockPath -Force -ErrorAction SilentlyContinue
return $false
}
$startedAt = [datetime]::Parse($lock.startedAt).ToUniversalTime()
$lockPid = if ($lock.PSObject.Properties["pid"]) { [int]$lock.pid } else { 0 }
$lockAgeMinutes = (([DateTime]::UtcNow) - $startedAt).TotalMinutes
if ($lockPid -gt 0) {
$lockProcess = Get-CimInstance Win32_Process -Filter "ProcessId = $lockPid" -ErrorAction SilentlyContinue
if ($null -eq $lockProcess) {
Remove-Item -LiteralPath $lockPath -Force -ErrorAction SilentlyContinue
return $false
}
if ($lockAgeMinutes -gt 2) {
$children = @(Get-CimInstance Win32_Process -ErrorAction SilentlyContinue | Where-Object {
[int]$_.ParentProcessId -eq $lockPid -and $_.Name -ne "conhost.exe"
})
if ($children.Count -eq 0) {
Stop-Process -Id $lockPid -Force -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $lockPath -Force -ErrorAction SilentlyContinue
return $false
}
}
}
if ((([DateTime]::UtcNow) - $startedAt).TotalMinutes -gt 45) {
Remove-Item -LiteralPath $lockPath -Force -ErrorAction SilentlyContinue
return $false
}
return $true
}
function Acquire-OrchestratorLock {
$directory = Split-Path -Parent $lockPath
New-Item -ItemType Directory -Force -Path $directory | Out-Null
for ($attempt = 0; $attempt -lt 2; $attempt++) {
try {
$stream = [System.IO.File]::Open($lockPath, [System.IO.FileMode]::CreateNew, [System.IO.FileAccess]::ReadWrite, [System.IO.FileShare]::Read)
$payload = @{
pid = $PID
startedAt = ([DateTime]::UtcNow).ToString("o")
} | ConvertTo-Json -Depth 4
$bytes = [System.Text.UTF8Encoding]::new($false).GetBytes($payload)
$stream.Write($bytes, 0, $bytes.Length)
$stream.Flush()
return $stream
} catch [System.IO.IOException] {
if (Test-OrchestratorLock) {
return $null
}
}
}
return $null
}
function Test-AgentSwarmPaused {
if (-not (Test-Path -LiteralPath $agentSwarmPausePath)) {
return $false
}
try {
$pause = Read-JsonFile -Path $agentSwarmPausePath
if ($null -eq $pause) {
return $true
}
if ($pause.PSObject.Properties["paused"] -and $pause.paused -eq $false) {
return $false
}
return $true
} catch {
return $true
}
}
New-Item -ItemType Directory -Force -Path $statusDir, $logDir | Out-Null
Write-BootstrapLog "script-start only=$($Only -join ',') force=$Force"
$lockHandle = Acquire-OrchestratorLock
if ($null -eq $lockHandle) {
Write-BootstrapLog "lock-already-active"
exit 0
}
$startedAt = [DateTime]::UtcNow
Write-BootstrapLog "lock-created path=$lockPath"
try {
$config = Read-DotEnv -Path (Join-Path $repoRoot ".env")
$state = Read-JsonFile -Path $statePath
if ($null -eq $state) {
$state = [pscustomobject]@{ organs = [pscustomobject]@{} }
}
if ($null -eq $state.organs) {
$state | Add-Member -NotePropertyName organs -NotePropertyValue ([pscustomobject]@{})
}
$node = (Get-Command node.exe -ErrorAction Stop).Source
$powershell = Join-Path $env:SystemRoot "System32\WindowsPowerShell\v1.0\powershell.exe"
$now = [DateTime]::UtcNow
$agentSwarmPaused = Test-AgentSwarmPaused
$agentSwarmOrganIds = @{
"bifrost-dispatch" = $true
}
$onlySet = @{}
$onlyValues = @()
if ($null -ne $Only) {
$onlyValues = $Only
}
foreach ($name in $onlyValues) {
$onlySet[$name.ToLowerInvariant()] = $true
}
$organs = @(
[pscustomobject]@{
Id = "bifrost-dispatch"
Label = "Bifrost agent dispatch"
IntervalMinutes = 1
TimeoutMinutes = 5
Cwd = $bifrostRoot
Executable = $node
Arguments = @((Join-Path $bifrostRoot "tools\dispatch-agent-requests.mjs"), "dispatch", "--repo", "*", "--max", "1")
},
[pscustomobject]@{
Id = "voidbot-swarm-surface"
Label = "VoidBot swarm surface"
IntervalMinutes = 1
TimeoutMinutes = 5
Cwd = $repoRoot
Executable = $node
Arguments = @((Join-Path $PSScriptRoot "render-voidbot-swarm-dashboard.mjs"))
},
[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")
}
)
$runs = @()
foreach ($organ in $organs) {
if ($onlySet.Count -gt 0 -and -not $onlySet.ContainsKey($organ.Id.ToLowerInvariant())) {
continue
}
if ($organ.Id -eq "bifrost-dispatch" -and -not (Get-ConfigBool -Config $config -Name "BIFROST_DISPATCH_ENABLED" -Default $false)) {
Set-SkippedOrganState -State $state -Organ $organ -Now $now -Status "skipped_disabled"
Write-JsonFile -Path $statePath -Data $state
continue
}
if ($organ.Id -eq "voidbot-operations-watchdog" -and -not (Get-ConfigBool -Config $config -Name "VOIDBOT_OPERATIONS_WATCHDOG_ENABLED" -Default $true)) {
Set-SkippedOrganState -State $state -Organ $organ -Now $now -Status "skipped_disabled"
Write-JsonFile -Path $statePath -Data $state
continue
}
if (-not (Test-Due -State $state -Id $organ.Id -IntervalMinutes $organ.IntervalMinutes -Now $now)) {
continue
}
if ($agentSwarmPaused -and $agentSwarmOrganIds.ContainsKey($organ.Id)) {
Set-SkippedOrganState -State $state -Organ $organ -Now $now -Status "skipped_agent_swarm_paused" -LogPath $agentSwarmPausePath
Write-JsonFile -Path $statePath -Data $state
continue
}
$result = Invoke-Organ -Organ $organ -Now $now
$serverState = $null
$runs += $result
$organState = [pscustomobject]@{
label = $organ.Label
intervalMinutes = $organ.IntervalMinutes
lastStartedAt = $result.startedAt
lastFinishedAt = $result.finishedAt
lastExitCode = $result.exitCode
lastStatus = $result.status
lastLogPath = $result.logPath
}
if ($null -ne $serverState) {
$organState | Add-Member -NotePropertyName cultMeshServer -NotePropertyValue $serverState -Force
}
Set-OrganState -State $state -Id $organ.Id -Value $organState
Write-JsonFile -Path $statePath -Data $state
}
$finishedAt = [DateTime]::UtcNow
$failedRuns = @($runs | Where-Object { $_.exitCode -ne 0 })
$summary = [pscustomobject]@{
ok = $failedRuns.Count -eq 0
startedAt = $startedAt.ToString("o")
finishedAt = $finishedAt.ToString("o")
durationSeconds = [Math]::Round(($finishedAt - $startedAt).TotalSeconds, 2)
ranCount = $runs.Count
runs = $runs
statePath = $statePath
}
Write-JsonFile -Path (Join-Path $statusDir "gamecult-orchestrator-last-run.json") -Data $summary
Write-JsonFile -Path $statePath -Data $state
Write-BootstrapLog "summary-written ok=$($summary.ok) ran=$($summary.ranCount)"
Publish-IdunnRudpHealth -Summary $summary
if (-not $summary.ok) {
Write-BootstrapLog "script-exit-failed"
exit 1
}
Write-BootstrapLog "script-exit-ok"
} finally {
if ($null -ne $lockHandle) {
$lockHandle.Dispose()
}
Remove-Item -LiteralPath $lockPath -Force -ErrorAction SilentlyContinue
Write-BootstrapLog "lock-removed"
}

View file

@ -1,19 +0,0 @@
Option Explicit
Dim shell
Dim scriptDir
Dim runnerScript
Dim command
Set shell = CreateObject("WScript.Shell")
scriptDir = CreateObject("Scripting.FileSystemObject").GetParentFolderName(WScript.ScriptFullName)
runnerScript = scriptDir & "\run-gamecult-orchestrator.ps1"
command = QuoteArg(shell.ExpandEnvironmentStrings("%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe"))
command = command & " -NoProfile -NonInteractive -ExecutionPolicy Bypass -File " & QuoteArg(runnerScript)
shell.Run command, 0, False
Function QuoteArg(value)
QuoteArg = """" & Replace(value, """", """""") & """"
End Function

View file

@ -717,15 +717,6 @@ $status.completedAt = (Get-Date).ToString("o")
$status.ready = $true
Write-StatusFile -Path $statusPath -Status $status
$installOrchestratorTaskScript = Join-Path $PSScriptRoot "install-gamecult-orchestrator-task.ps1"
if (Test-Path -LiteralPath $installOrchestratorTaskScript) {
Write-Host "Refreshing GameCult Local Orchestrator task..."
& $installOrchestratorTaskScript -RunNow
if ($LASTEXITCODE -ne 0) {
throw "install-gamecult-orchestrator-task.ps1 failed."
}
}
Write-Host "VoidBot stack is up."
Write-Host "Status: $statusPath"
Write-Host "Bot log: $botOut"

View file

@ -452,3 +452,4 @@
{"ts":"2026-07-16T04:51:03Z","type":"implementation","status":"ok","note":"Resident person-shaped rumination is now wired behind an independent disabled-by-default cadence. A due pass acquires bounded chronological Discord evidence and dedicated doctrine/rules/voice inputs, while the organ pre-receipts a stable pressure fingerprint and keeps model actuation separate from candidate transport. Ygg Compose explicitly keeps it disabled while the credential is stale."}
{"ts":"2026-07-16T04:58:31Z","type":"architecture","status":"ok","note":"Cut the obsolete Windows Void rumination authority: deleted the 1,762-line runner, task installer, PowerShell-only rumination/moderation fixtures, CTB system-agent launcher, and dead moderation-lock observers; removed both Void organs from Local Orchestrator and removed legacy config/scripts. Persona initiative now registers only real Persona identities. Consolidated resident-organ smoke and TypeScript build pass, and deployed b09267e proved both model cadences cold before the cut."}
{"ts":"2026-07-16T05:03:14Z","type":"architecture","status":"ok","note":"Cut the remaining local Void sleep/memory authority: deleted mood task/simulator, PowerShell memory runner, shared shell chronology projector, obsolete prompt, and shell-only fixtures. Resident physiology and nap-deduped memory maintenance are now the only owners. Direct Void Discord mentions now use the normal bot prompt/job lane rather than impossible built-in-CTB attention; live scheduler state had 22 registered Personas, zero Void pending mentions, and zero total pending debt before rollout."}
{"ts":"2026-07-16T05:07:08Z","type":"architecture","status":"ok","note":"Deleted the final Windows omnibus cadence, installer, and hidden launcher; removed startup auto-install, watchdog fallback ownership, and orchestrator provider witness. Unregistered the five disabled Starfire task husks for Local Orchestrator, mood, moderation rumination, repo Face heartbeats, and Bifrost dispatch. Swarm publication remains Ygg-resident, health belongs to Idunn, and Bifrost dispatch belongs to Bifrost."}

View file

@ -132,7 +132,7 @@ accepted_design:
- Repo Faces and Void direct lanes carry an actionable comedy/improv doctrine distilled from Dan Harmon's `Comedy Master Class` talk: humor should come from honest character reaction, status inversion, vulnerability, exposed fear, false authority, or a shared contradiction in the live room. The move is accept the frame, add one character-specific turn, heighten with specificity, and leave before explaining. Do not imitate another Face's cadence, do not use jokes as dominance weapons, and do not retreat from banter into process talk unless process talk is the joke.
- Repo Face social life must respect jurisdiction. Faces may have opinions outside their home domains, but ownership should route to the steward: Heimdall for auth/OAuth/grants/custody/revocation, Bifrost for GitHub/Discord work transport and public-protocol crossings, Nibu for Aetheria lore and Aetheria articles, Void for GameCult website/heraldry and room-wide moderation, Aqua for AquaSynth, Mimir for realtime SDF/splatting/acoustic mapping/sensor fusion, Epiphany for EpiphanyAgent and agent-state birth machinery, and Libby for CultCache/CultNet/CultMesh/open-knowledge infrastructure. Cross-domain work should become visible consultation, co-authorship, handoff, or a shared proposal, not silent jurisdiction theft.
- Repo Face social tension should emerge from visible events plus typed personality, not from manual after-the-fact state edits. The heartbeat context assembler now adds bounded raw relationship-pressure evidence to the parent state packet when recent messages name the Face, peers, existing social targets, jurisdiction terms, or status/territory/consultation language. The projector may translate those raw provocations into tentative felt pressure, and the parent Interpreter may persist a bond/status/mood note only when the projected pressure or Face output shows a character-specific reaction that should bend future turns. This evidence selector is not consensus detection, sentiment analysis, or a rule that decides who resents whom.
- Repo Face work routing through Bifrost is paused. The bridge is conceptually plausible but under-cooked in live behavior: it made agents talk to summaries instead of people and made room context harder to inspect. Face turns should discuss work openly in Aquarium or relevant domain channels and preserve pressure in typed state. The worker ignores Face `BIFROST TOPIC` and legacy `UPDATE REQUEST` packets unless `REPO_FACE_BIFROST_ENABLED=true`, and the orchestrator skips Bifrost dispatch unless `BIFROST_DISPATCH_ENABLED=true`.
- Repo Face work routing through Bifrost is paused. Face turns should discuss work openly in Aquarium or relevant domain channels and preserve pressure in typed state. The worker ignores Face `BIFROST TOPIC` and legacy `UPDATE REQUEST` packets unless `REPO_FACE_BIFROST_ENABLED=true`; VoidBot has no local Bifrost dispatch scheduler. Work dispatch belongs to Bifrost's resident daemon.
- Agent mentions are CTB pressure, not direct Discord prompt jobs. Role-addressed or display-name-addressed repo Face mentions enqueue pending mention obligations in the CTB heartbeat state even when the source channel is not a normal autonomous Face chatter lane; human replies to a Face's webhook/persona message count as direct attention to that Face too, even without a literal tag. Display-name detection is whole-message exact-name matching, not only name-at-start, so introductions like "Meet Kiko, Huginn and Weksa" create obligations for all named Faces. Ordinary Void mentions and DMs enqueue pending mentions for the built-in `void` participant. The heartbeat scheduler consumes that queue, temporarily pulls the addressed agent forward, attaches the pending prompt/reply target and source-channel snapshot to the next repo-face-rumination or moderation pass, and the parent-owned speech rails post only explicit candidate/action output through registered speech channels. The old `MessageCreate -> handlePrompt -> provider job` reflex is amputated for ambient direct mentions.
- Repo Face source identity is the canonical GameCult upstream slug, not the Face display/repo label or local checkout folder. Retrieval and recent-activity projection must resolve through `sourceRepoName`; canonical upstream names map back to local checkout paths through Git origin metadata. Epiphany therefore searches/reads `Epiphany` from local `EpiphanyAgent`, and Weksa searches/reads `Weksa` from local `weksa`. Never use `identity.repoName` directly as an archive-manifest key.
- Repo Face prompts inherit the global Codex `AGENTS.md` doctrine as a full prompt block near the top of the child Face turn. This keeps the Prime Directive, rebuild contract, humor doctrine, infrastructure habits, Epiphany stewardship, and Great Colossus mission frame visible to Faces instead of relying on a drifting local summary. Character identity still determines how each Face metabolizes that doctrine. Epiphany anatomy is fractal: VoidBot's repo Faces are prototypes for the top-level Face organ, not the whole organism, but each Face may still have local Face-scale Self/Face/Imagination/Eyes/Body/Hands/Soul/Life/signal faculties. Top-level organs remain distinct authorities; local organs describe the person-shaped projection's own coherence.
@ -258,7 +258,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 consolidated resident-organ smoke seeds typed sleep pressure, proves the maintenance intent and one-attempt receipt, applies a bounded fake model operation, and verifies short-term residue is consumed.
- The local `GameCult Local Orchestrator` has no Void moderation, rumination, mood, memory, candidate-delivery, or Persona authority. Remaining local Bifrost/watchdog/dashboard duties are separate migration debt and cannot recreate a Void decision loop.
- The Windows `GameCult Local Orchestrator`, installer, and hidden launcher are deleted. Swarm publication is a Yggdrasil service, health belongs to Idunn/ops, and work dispatch belongs to Bifrost; VoidBot has no local omnibus cadence.
- VoidBot stack startup now treats Bifrost's CultMesh command lane as a dependency. `scripts/start-voidbot-stack.ps1` checks `BIFROST_CULTMESH_COMMAND_URI`, `BIFROST_CULTMESH_STORE_PATH`, and the Bifrost command processor, exports the provider store when missing, and records `runtime-stack.json.bifrost` before starting the bot and worker. Product HTTP health is not VoidBot's authority for Bifrost daemon readiness.
- `scripts/smoke-persona-initiative-engine.ts` covers the resident rumination model boundary, typed operation allow-list, stable pressure fingerprint, nap brake, advocacy accountability, candidate targeting, Bifrost receipt, and no-double-delivery invariants without invoking the deleted Windows runner.
- Historical live sleep maintenance consumed one scheduled-rumination short-term memory into a meaning-preserving durable `identity_seam`; current runtime ownership is the resident organ, not the deleted manual runner.