72 KiB
VoidBot Fresh Workspace Handoff
VoidBot is no longer small enough to trust chat scrollback as its only memory. Use the canonical state files first, then touch code.
What To Trust First
For the current project shape and next move, trust these in order:
state/map.yamlnotes/fresh-workspace-handoff.mdnotes/voidbot-current-system-map.mdnotes/voidbot-implementation-plan.mdstate/evidence.jsonl
Git history and smoke artifacts carry routine proof. state/evidence.jsonl should carry only belief-changing records, not every tiny triumph with a timestamp stapled to it.
Current Shape
- VoidBot's deployed runtime lives on Yggdrasil. Idunn owns deployment from upstream
mainand proves the served commit through/srv/voidbot/deploy/deployment.env. This workspace is for development and diagnostics; it is not a deployment target. Do not look for, create, or revive a local VoidBot deployment. To deploy a completed change, push the intended commit to upstreammain, then verify Idunn convergedDEPLOYED_REVISIONand the Yggdrasil service set. - Discord bot and worker are live.
- The unattended repo Face/native Persona CTB swarm now runs on Yggdrasil. Resident
apps/persona-schedulerinsidevoidbot.serviceowns the serialized Persona pulse and queues turns through Postgres for the worker. The WindowsGameCult Local Orchestratorno longer owns Persona scheduling. Void moderation, mood, watchdog, and Bifrost dispatch are still separate migration work and must not be folded into this scheduler. - 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": trueor is malformed, the Face 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.
- Qdrant owns history and source vectors.
.voidbot/owns archives, artifacts, logs, status files, and backups.owner_codexandlocal_llmare the active reply lanes.- The ordinary bot/worker/provider/RAG/Postgres/Qdrant foundation still deserves to survive.
- The moderation/mood/private-self-state foundation now runs on the typed store boundary. Treat it as live but still under watch until ordinary unattended rumination and sleep cycles have shown they do not produce duplicate speech, stale short-term residue, or ungrounded memory writes.
- CultCache-backed self-state uses the
.ccextension. The active store path is.voidbot/private/void-self-state.cc. - Legacy self-state debris from older typed shapes is decode-only now.
void.moderation_cursor.userStatuses, oldvoid.persona_affect, and affectstressResponsesmay be read during migration, but the live repair/write path promotes affect tovoid.face_affect, drops ownerless old keys, and deletes the old persona-affect alias. - The smallest coherent target is one polymorphic CultCache-backed state authority with typed document kinds plus typed mutation tools: agents propose small operations, and the state service validates/applies them. Whole-state JSON editing is rejected as a durable mutation path.
- Repo Faces can now use a separate shared CultCache scratch store through
read_shared_document,create_shared_document, andupdate_shared_document. These tools are present only in the Face-turn MCP allow-list, stamp the active Face identity server-side, and require an expected revision for updates. The capability prompt is deliberately generic; it does not prescribe a use case. - The typed boundary is in code at
packages/core/src/void-self-state-domain.ts: it declares the global document kinds and strict operation payload schemas used by the live runners. - Read-only projection is now owned by
packages/core/src/void-self-state-projection.ts.loadVoidSelfStateis a thin loader that reads typed.ccdocuments and delegates the agent-facing summary plus nap/reply-mode projection to that module. - Typed operations are applied through
packages/core/src/void-self-state-service.tsandscripts/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.mjsreads repo cursors from the typed.ccstore. Legacy.jsoncursor arguments are only mapped to a sibling.ccpath when needed by old invocation surfaces.scripts/run-void-moderator-rumination.ps1has been rebuilt as a typed rumination runner. It builds a typed context packet, loadsprompts/void-moderator-rumination.md, runs Codex rumination, and applies only the JSON operation payloads that come back throughscripts/void-self-state.mjs. Cursor and receipt bookkeeping remain parent-owned.- 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 like5 minutes ago, including recent Discord messages, repo activity, receipts, memory timestamps, and sleep/speech timing. scripts/simulate-void-mood.mjsowns 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-maintenancefor smoke runs that must not call Codex.npm run moderation:memory-maintenance:sleep-fixturenow 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 proposeapply_memory_distillation, and verifies short-term residue is gone while the durable memory keeps subject, claim, tension, action implication, and anchor.- Persona scheduling is no longer part of the hidden Windows pulse. Yggdrasil runs
apps/persona-scheduleras a resident compose service insidevoidbot.service; it serializes CTB ticks and leaves execution to the worker.GameCult Local Orchestratorstill describes legacy/local ownership for other organs and must not be treated as the Persona owner. scripts/start-voidbot-stack.ps1now treats Bifrost's CultMesh command lane as a startup dependency. It readsBIFROST_ROOT,BIFROST_CULTMESH_COMMAND_URI, andBIFROST_CULTMESH_STORE_PATH, exports the provider store when missing, verifies the command processor exists, and records the result underruntime-stack.jsonasbifrost.- 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.
Bifrost owns Reddit transport/thread access and receipts; Void owns
moderation judgment for posts/comments there just as it does for Discord.
Repo Face turns can now route a
REDDIT THREADaction block through Bifrost for public ideas, article presentations, and Persona-authored organizing posts. Void is still GameCult's herald there, but built-in Void rumination needs a Reddit delivery target before autonomous Void subreddit posting is as complete as the repo Face path. npm run moderation:rumination:fixtureproves 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:speech-fixtureproves queued speech has a parent-owned action loop: fake Codex queues one candidate with a delivery target, fake Discord send writes a receipt, and the parent runner marks the candidate spoken through typed state. The child prompt now forbids direct send-script calls.- The first live sleep-maintenance gate passed on real state. A manual typed nap ran
scripts/run-void-memory-maintenance.ps1, consumed the scheduled rumination short-term memory with oneapply_memory_distillationoperation, and leftthoughtMemory.shortTermempty while preserving the durable identity seam with target, claim, tension, action implication, and anchors. - A later live-loop inspection found one stale answered-case summary, four stale short-term memories, and an unbounded rumination/memory-maintenance failure mode. The current hardened path now verifies candidate delivery postconditions, normalizes answered-case wording on receipt/closure, uses process-aware locks plus Codex timeouts, and a fresh memory-maintenance pass reduced live short-term memory back to zero.
npm run moderation:rumination:nap-skip-fixtureproves awake rumination does not keep making fresh short-term residue during sleep: when the typed sleep cycle is napping and there are no new room messages or open cases, the runner writes[]and exits before invoking Codex.- The old memory-organ script family, legacy moderation-state template, and legacy moderation context exporter have been deleted. Do not revive their cleanup, agency, dream, vector-incubation, or mirror-repair behavior as compatibility scaffolding.
scripts/run-void-memory-maintenance.ps1is the first replacement memory-maintenance organ. It builds a bounded context from typed memories/incubation/candidates/runtime, loadsprompts/void-memory-maintenance.md, and applies only allowed memory/incubation/candidate typed operation proposals throughscripts/void-self-state.mjs. In sleep mode, memory pressure setssleepDirective.forceDistillation; a real model pass returning no operations under pressure fails visibly.- Rumination does not write durable memory. It can only emit
record_short_term_memory; sleep maintenance must promote short-term records withapply_memory_distillation, merge them into incubation and prune them, or prune them outright. A real sleep pass that leaves short-term records behind fails. - Short-term rumination memory is clustered at the typed service boundary now. When a new
record_short_term_memoryoperation repeats the same target/topic pressure, the service keeps one provisional memory, merges anchors/tags, preserves a stable source id for sleep, and updates the thought instead of stacking duplicates. This is meant to protect deepening, not suppress it: low-saturation or newly anchored thoughts should keep developing inside the cluster until they stop changing.npm run moderation:short-term-clustering:fixtureproves the guard. - Long-term memory is durable, not immutable.
revise_durable_memory,retire_durable_memory, andcrystallize_memory_into_identitymake memory reconsolidation explicit;npm run moderation:memory-lifecycle:fixtureproves a short-term thought can promote, revise, retire superseded durable records, and crystallize into an identity memory plus self-profile value. - Agency has a typed pressure surface now:
upsert_agency_pressure/retire_agency_pressureown discomforts, active tensions, self-advocacy requests, and world-advocacy requests. Candidate interventions are possible speech/actions emerging from pressure, not the pressure itself.npm run moderation:agency-pressure:fixtureverifies the pressure enters.cc, renders in self-state summary, and affects mood speaking pressure. - Strong advocacy pressure now has to account for its own mouth. The rumination runner derives
speechPressureObligationsonce active self/world advocacy reaches 0.55 intensity, and only a live queued candidate counts as release for that obligation. A pass may queue/defer a candidate or cool/retire the pressure, but silent[]fails visibly and an older spoken candidate does not buy permanent absolution.npm run moderation:rumination:speech-pressure-fixturecovers the silent, spoken-history, and queued-candidate paths. - Severe safety evidence now has the same no-silent-advance wall. The moderation heartbeat projects
urgentModerationWitnessesfrom recent Discord history for credible violence, weapon, robbery, stalking, or sexual-boundary evidence, and the parent rejects silent[]unless an existing open case or new moderation operation accounts for the witness.npm run moderation:rumination:urgent-safety-fixturestill covers the older rumination guard, whilenpm run moderation:heartbeat:policy-fixtureproves the heartbeat-only policy path. - Urgent safety handling can now enforce through a parent-owned Discord action path.
VOID_MODERATION_ENFORCEMENT_MODE=policyenables instant bans and three-strike bans according to the explicit table inconfig/discord-server-rules.md; the child only opens typed cases withinfringement:<type>plus one moderation disposition tag. The child prompt still does not own raw sanction authority. - Deferred candidates are not delivered speech and no longer satisfy active advocacy pressure by themselves. The runner now delivers existing queued candidates with delivery targets, not only candidates proposed by the current model pass. The first live repair used Metacrat's explicit channel permission to move the stranded AquariumSynthHost candidate from deferred/no-target to queued/channel-target, then delivered it through the normal bot transport and recorded a typed speech receipt.
- Parent-owned delivery now also checks canonical typed speech receipts before using the mouth. If a queued candidate targets a Discord reply that already has a receipt, the runner retires it instead of answering the same message twice in slightly different prose. Typed state writes also normalize historical receipts to keep the earliest canonical reply per
channelId + replyToMessageId.scripts/smoke-void-rumination-speech-fixture.ps1now seeds a prior receipt and proves both same-pass sibling retirement and already-answered target retirement. - Speaking pressure is allowed to refresh while moderation is active.
scripts/simulate-void-mood.mjsstill skips sleep-cycle mutation and memory maintenance under a recent moderation lock, but it now reloads typed state and appliesupdate_speaking_pressurefirst, so fresh canonical speech receipts updatelastSpokeAt, damping, andneedToSpeakeven during long rumination runs. - Most of the deterministic legacy translation and retention code is recent compensator cruft from failed attempts to manually clean an exploded state file. Do not preserve it as an earned organ. Architect the state boundary so that bad state cannot enter, and keep only narrow historical migrations for old debris.
- The desired agency shape is typed state operations plus derived/model-owned policy, not a permanent sermon generator in maintenance code.
- Sleep/distillation now has a runner boundary, prompt contract, fixture coverage, and one live-state pass. The invariant is fixed: concrete subject, claim/question, anchors, live tension, and future-action implication survive compression. Bulk can die. Meaning does not get put through the office shredder and called a dream.
- Typed memory operations now enforce the first ingress wall for the next brain: rumination can only use
record_short_term_memory, while sleep maintenance promotes withapply_memory_distillationor drops residue withprune_short_term_memories. Memory writes require a concrete target, claim or question, tension, action implication, and anchors unless the operation explicitly tagsanchor:missing. This does not fix old state. It prevents new summary-only mush from being accepted as memory. - The owner Codex lane is no longer one swollen file; orchestration, runtime/parsing, rendering, and shared helpers are split under
packages/providers/src/owner-codex-*.ts. - The local Ollama lane is no longer one swollen file; orchestration stays in
packages/providers/src/local-llm-provider.tswhile prompt/rendering, tool-loop helpers, and shared types/constants live underpackages/providers/src/local-llm-*.ts. - The Discord bot lane is no longer one swollen entrypoint; gateway wiring remains in
apps/bot/src/discord-bot.tswhile command/prompt handlers and Discord-shape support helpers now live underapps/bot/src/discord-bot-*.ts. - The worker MCP surface is no longer one swollen server file; stdio bootstrap stays in
apps/worker/src/mcp-server.tswhile context bootstrap, resource registration, tool registration, Discord notify helpers, and shared formatting/schema helpers live underapps/worker/src/mcp-server-*.ts. - Interaction-memory logic is no longer one thousand-line slab; event/tone analysis, shared constants, and profile synthesis are split across
packages/core/src/interaction-memory-*.ts. - Ephemeral room-reading now comes from a quick
think=falselocal Ollama sidecar inpackages/core/src/situational-social-read.tsinstead of hand-built phrase heuristics, and the inferred reads are recorded as audit events so they can be aggregated into richer profiles later without immediately mutating durable identity state. - State storage is no longer one persistence omnibus;
packages/core/src/state-storage.tsis now a thin factory over domain-specific Postgres/file store modules plus bootstrap/migration helpers. - Source archive storage is no longer one giant JSON monolith;
packages/rag/src/source-document-archive.tsnow keeps a small manifest at.voidbot/rag/source-documents.jsonand per-repo shards under.voidbot/rag/source-documents.repos/. - Ops health, backup verification, offsite sync, and dashboard surfaces already exist.
- 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.
- Source repo discovery now reconciles automatically during
npm run stack:startand through the watchdog path, so new repos underSOURCE_REPO_ROOTstop 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.
- The moderation heartbeat organ is invoked every orchestrator pulse against
.voidbot/private/void-self-state.ccwithprompts/void-moderation-heartbeat.md, which exposes only rules, recent history, urgent witnesses, and infringement history. The moderation rumination organ is still invoked byGameCult Local Orchestratorfor Void's slower person-shaped participation and possible announcement turns; its prompt lives inprompts/void-moderator-rumination.md. Treat both files as sensitive behavior surfaces, not incidental script text. - The moderation loop now uses the typed context packet from
scripts/run-void-moderator-rumination.ps1; legacy Ghostlight-shaped JSON runtime fields are not the active memory surface. - Repo-weather sweeps are now incremental rather than a rolling four-day scrapbook. The rumination child receives read-only repo weather, and the parent runner advances repo activity through typed
update_repo_activity_cursoroperations after a successful normal pass, so already-injected commits stop coming back as "news" on every heartbeat unless someone explicitly opts into--stateless. - Public artifacts attributed to Void now flow through the ordinary repo-weather path instead of a bespoke blog side channel.
repoActivity.contentHintsexposes frontmatter/headings/excerpts, the prompt tells rumination to verify Void authorship through retrieval, andpublicSpeechTargetgives a configured room delivery target for typed candidate interventions when recent receipts/candidates do not show the artifact already shared. - Novelty still needs two bars, not one: a thought can be new to the room while still being stale to Void. The replacement design should derive that judgment from typed memory/incubation state and model reasoning, not from the deleted compensator stack.
- Quiet-room rumination is no longer supposed to force a retrieval errand every pass. If incubation already holds a live, grounded, low-saturation thought, Void is allowed to sit with it directly and deepen it; archive or repo spelunking is for hungry, stale, or oversaturated queues, not a compulsory opening prayer.
- The loop is now less shy about that repo weather: if a fresh convergence across active repos survives a pass or two and still feels room-native, it should bias toward a concise herald note or at least a medium-priority candidate intervention instead of filing the thought under private wallpaper.
- The sharing gate should stay less precious, but future ripe-thought sharing must be expressed as typed
queue_candidate_interventionoperations or direct speech with parent-owned receipts. - The moderation loop no longer gets to assume Discord history will faithfully show it its own mouth. Canonical typed state carries
void.speech_receipts, and direct-question dedupe should consult those receipts before it re-answers a still-visible message. - Chronological moderation polling is now supposed to be exact once a reviewed cursor exists. The runner hands the unattended child one concrete
export-recent-discord-history.mjs --after <saved timestamp>command for the pass and explicitly forbids widening that back into a bootstrap--hours 6nostalgia crawl unless there is genuinely no cursor yet. - 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. - 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 postshould not become durable memory. - Quiet-room exploration should now prefer underworked terrain when the usual seam is saturated.
bridge.source_coverageandbridge.refractory_topicsexist to push the next pass toward less-chewed repo families, archive years, or channels instead of rewarding repetition with another supposedly ripe synthesis. - During naps, the moderation loop is supposed to turn inward: distill/prune memories against active goals through typed memory operations, refresh typed scheduled runtime, and only break the nap for real smoke or an unusually novel thought worth muttering into the room.
- Sleep is meant to be lossy about bulk, not about meaning. The live sleep path now preserves concrete repo-bound or room-bound meaning through typed distillation; keep inspecting ordinary cycles for abstraction sludge.
- Awake rumination is now supposed to do the actual philosophy from those surviving seams. The moderation doctrine tells quiet runs to begin with seam-thinking when incubation is live: ask what principle the seam is circling, what tension keeps it honest, and what embodiment consequence would cash it out before reaching for more retrieval.
- Identity crystallization is still desired, but the old hard-wired path is gone. Rebuild it as model-owned judgment crossing typed self-profile, thought-memory, and candidate-intervention operations.
- Crystallization is not meant to be mute. When rebuilt, it should queue typed candidate interventions when a first-person share is warranted.
- Those crystallized-thought drafts can now be marked
mustEventuallyShare, and ordinary candidate-intervention pruning is supposed to preserve them. The loop may defer for timing if the room is busy, but not just quietly forget what became doctrine. - That awake rumination is now explicitly biased toward concrete project fascination. If a thought stays too abstract for too long, the loop is supposed to tie it to a concrete system: Aetheria-Economy gameplay systems, lore-vault structure, Aquarium frontend work, Epiphany swarm organs, VoidBot's own architecture, or some other specific repo/subsystem instead of another polished sermon about engineering virtue.
- Thought labels and curiosity budget must stay cut away from keyword compost. Incubating thoughts should name actual claims, questions, or fascination targets, and curiosity should favor concrete map-changing terrain over whichever seam already feels warm.
- The live moderation state has now had one real translation pass too, not just new code draped over old fossils. Legacy slash-label seam memories and dream themes are rewritten through the new thought synthesizer, obvious duplicates get merged or pruned, and the active queue is rebuilt from the cleaned substrate instead of the old keyword graveyard.
- That cleanup is now harsher and less sentimental. Sleep-side consolidation trims semantic memory harder, cuts dream memory down, throws away excess repo/archive/runtime residue, and explicitly keeps the freshest surviving records after sort instead of accidentally preserving the oldest fossils in the file.
recent-preoccupationandquiet-room-statusare not durable thought kinds, person-singleton labels do not get to crystallize into doctrine, and boilerplate likelooks like a live seamis template smell.- The old moderation-agent state and its top-level mirrors are not source material for the next brain. Treat them as failed-boundary evidence only.
- The replacement sleep/distillation path must stop "helping" already-good memories into abstraction sludge. A repo-bound memory like
AquariumSynthCSharp: Workflow cannot own the bodymust survive with subject, claim, anchor, tension, and implication intact. - Values and personality activations still need an autonomy path, but it must be rebuilt through typed operations and model-owned judgment rather than legacy runtime buckets.
- The important distinction is outward too, not just inward. Self-directed tensions can ask for prompt/memory/runtime surgery, while world-directed tensions can turn repo/lore/room opinions into real requests or first-person interventions when the same objection survives more than one pass.
- The intended shape now is: quiet runs can follow one seam for several passes, connect repo motion to lore or philosophy if the evidence supports it, and only surface with the juiciest version once the incubation queue says the thought has actually grown teeth.
- Ambient Discord mentions are no longer a direct reply organ. Role/display-name Face mentions, human replies to a Face's webhook/persona message, and ordinary Void mentions/DMs append pending obligations to
REPO_FACE_HEARTBEAT_STATE_PATH; the CTB scheduler pulls the addressed agent forward and passes the pending prompt/reply target into the next Face rumination or Void moderation turn. Display-name matching is exact-token matching across the whole message, so group introductions like "Meet Kiko, Huginn and Weksa" queue every named Face instead of requiring the message to begin with one name. Mention detection can happen outside the regular autonomous Face chatter lanes, but public speech still goes through the registered channel/action rails with source-channel transcript attached. Slash commands and explicit owner queue tools may still use provider jobs, butMessageCreateno longer falls straight intohandlePrompt. - The moderation loop now speaks through
node scripts/send-discord-message.mjsusing the local bot token instead of approval-gated side-effecting MCP tools, so unattended runs can DM or post without Codex pausing for permission every time it wants to open its mouth. That helper and thepost_discord_messageMCP tool now also support a shared guild-channel webhook pipe with per-messagepersonaNameand optionalpersonaAvatarUrl, so swarm agents can speak as themselves without needing separate Discord bot identities. - Repo identities now have a cleaner address/speech split. A private registry at
REPO_DISCORD_IDENTITIES_PATHmaps identity id/repo/display name/avatar/role/channel allow-list. Discord users can address a Face by role mention or exact display-name mention; Codex agents still speak throughpost_repo_identity_message. - Void still moderates and participates everywhere the normal bot lanes allow. The swarm/repo Faces are scoped to text
#aquarium(1501196543150264332) so their lively heartbeats and role replies do not flood general. The old general channel id is113786069023064068; do not reuse it as a Face heartbeat default. - Repo Faces now have topic/channel speech permissions. Aquarium is the deliberately low-threshold general-musing lane for all Faces; they may post compact casual thoughts, jokes, curiosities, and half-formed fascinations there without a work artifact. Domain channels are also allowed when the topic matches: Nibu can use narrative/gamedesign/concepts/development, Aqua can use music/soundtrack/development, Mimir can use development/programming/visual-effects/machine-learning/unity3d, and Epiphany can use development/programming/machine-learning. The CTB scheduler folds per-topic speed multipliers into Face initiative, and the heartbeat prompt includes recent cross-channel context so agents can jump into non-Aquarium chatter when their thoughts line up.
- Repo identities also have Face state now. The bot resolves each identity to
.voidbot/private/repo-faces/<identity>.ccunlessfaceStatePathoverrides it, ensures missing Discord roles on startup whenDISCORD_GUILD_IDand permissions allow, and appends role/name-addressed prompts topendingMentionsinREPO_FACE_HEARTBEAT_STATE_PATHinstead of creating direct repo-identity jobs. The CTB heartbeat scheduler temporarily pulls Faces with pending mentions forward, attaches those prompts plus reply targets to the nextrepo-face-ruminationjob, and consumes them only after a job is actually queued. Worker auto-posting stays suppressed forrepo-face-rumination; the child must emit a parent-owned action block for public delivery. - Repo Face prompts now include an identity doctrine block. Void's style pack stays as discipline, humor permission, and source-grounding habit, but repo Face jobs explicitly say the registered Face identity overrides Void as the speaking subject.
- Repo Face embodiment is now doctrine, not local flavor. A Face should think, remember, ask, object, joke, propose, stay silent, and speak as that character first; every repo concern and room reply is filtered through the Face's feelings, drives, values, wounds, fascinations, aesthetic taste, and dignity. Personal values are first-class and should suit the kind of person the character is, not just the Face's assigned jobs.
- A sub-agent-monitored strengthening pass has now been applied to the established active agents: Aqua, Nibu, Mimir, and Void. Epiphany has since joined the crew with a completed repo-local VoidBot birth surface and should receive the same strengthening treatment once her first live turns show what needs sharpening. The resulting typed state should be treated as personality state, not ornamental notes.
- Aqua's state now emphasizes musical truth with warmth: a tiny listening steward with standards, reference-backed playability pressure, and protection against both sterile DSP accounting and empty mascot cheer.
- Nibu's state now emphasizes dangerous mutual benefit: manufactured companion injury, self-authorship under coercive dependency, precise abrasion, continuity-by-replacement horror, and the four-bone worldbuilding method of mechanism, cost/failure, institution/faction, and leash.
- Mimir's state now emphasizes keeper-of-the-well embodiment: trusted signals, calibration as oath-work, drift accounting, old memory, and refusal to speak like a dry technical summary.
- Epiphany's state emphasizes machine-saint coherence: cute, bubbly, self-obsessed, pushy, relentlessly hard-working, and obsessed with typed truth. Her jurisdiction is EpiphanyAgent itself: CultCache/CultNet, typed state, Codex bridge boundaries, birth/init rites, review gates, graph-shaped memory, low-copy runtime contracts, heartbeat physiology, and cutting duplicated authority before it learns to beg.
- Face affect is now a first-class typed state surface under
void.face_affect, shared by Void and repo Faces. It stores needs, social bonds, status reads, and mood dimensions so substrate concern, attention hunger, agency frustration, recognition needs, territoriality, rest/play pressure, friendships, rivalries, envy, pride, irritation, pampering, neglect, bypasses, and consultation status can become state instead of being performed only through prompt prose. Repo Face CTB pulses fold affect into speaking pressure, while agency pressure still owns concrete asks. - Affect has an MCP-surface guard now.
get_voidbot_runtime_infoexposes the running MCP process id, workspace, self-state schema fingerprint, and registered document types.npm run agents:repo-face-affect-mcp-fixture -- --identity nibuand the same command formimirspawn the built VoidBot MCP server, assert that runtime fingerprint includesvoid.face_affect, and callread_repo_face_state, provingtypedState.faceAffectis readable through the tool surface that agents actually use. If the currently mounted Codex MCP tool still saysNo schema is registered for persisted document type "void.face_affect"while the fixture passes, that process is stale; reinstall/restart the MCP server rather than editing the state files. - Void's typed self-profile now carries behavior-anchored selfhood, answer-first banter, and substrate self-respect; the prompt surfaces now also tell rumination to name the personal value being protected before writing memory or speech.
- Repo Face turns must stay out of the generic owner-reply trap.
packages/providers/src/owner-codex-render.tsrecognizes current Epiphany Face turn prompts, labels them as repo Face turns, forbids genericHANDOFF_SENTINELoutput for them, and tells the child to use Face action rails instead of bailing when Face-state or source tools are unavailable. - Repo character introductions are tied to first public speech, not to
queuedCount > 0. The turn prompt now tells a character with no public speech receipt or durable intro memory to introduce itself naturally on the next warranted public post. Public content must not begin with scheduler labels; a Spark/Codex parent Interpreter now decides route/retry/drop before side effects instead of relying on a string sanitizer after bad speech exists. - Repo Face identity is now seeded into the typed
.ccself profile during initialization and passed through repo Face MCP state reads/writes. The renderer may still provide identity context, but raw state should not quietly sayVoidfor a non-Void Face. - Repo Face prompts now also grant advisory repo-change agency: Faces may build long-running maps of their repos and propose concrete changes, but Discord jobs stay read-only and must ask for human consensus before implementation changes canonical repo material. Once a proposal is concrete enough to review, it belongs on GitHub as a draft PR; Discord is for announcing and arguing around that artifact, not replacing it.
- Repo Face draft, review, article, Reddit thread, and Discord speech lanes now cross through explicit parent-owned action blocks before Bifrost-owned commands. Bylined articles use the Interpreter-owned
ARTICLEDSL block with title/description/author/date/tags/path/body fields; deterministic worker code renders and validates YAML frontmatter, strips model-supplied body frontmatter, and routes Aetheria articles underAetheria/Articles/or non-Aetheria blog posts underGameCult/Blog/ingamecult-site. Subreddit threads useREDDIT THREADand Bifrostreddit-postwith Persona flair. LegacyVOIDBOT_REPO_IDENTITY_ARTICLEJSON is no longer accepted. Proposal PR and PR-comment compatibility sentinels still exist. Bifrost owns governed crossings because it is GameCult's governance/labor platform and public-process bridge, not because it is a convenient script bucket. Heimdall remains the OAuth/account/capability gate. Cross-daemon targets should be stored as CultMesh URIs discovered/announced through Odin provider/interface catalogs; do not restoreBIFROST_BRIDGE_BASE_URL/token or script-path shortcuts as the canonical transport. - Repo Face Bifrost/governance routing is paused. Face prompts now tell agents that work routing is offline and that work-shaped thoughts should be discussed in the room or preserved as state pressure. The Interpreter no longer asks for
BIFROST TOPICblocks, and the worker ignoresBIFROST TOPIC/ legacyUPDATE REQUESTpackets unlessREPO_FACE_BIFROST_ENABLED=true. This is deliberate: the live bridge made agents talk to terse governance summaries instead of people. - Repo Face state projection is no longer allowed to be deterministic schema slurry.
scripts/run-repo-face-heartbeats.tsbuilds a compact state packet, then calls the model prompt atprompts/repo-face-state-projector.prompt.mdto turn it into character-facing prose before the Face sees it.REPO_FACE_STATE_PROJECTOR_ENABLED=falseexists only as an emergency isolation switch; both the model output and fallback packet reject schema/prompt-construction leaks such asgrants:,jurisdictions:,Face of, repo paths, and "do not prompt" phrasing. - First role-addressed chat also initializes the target repo's local
.voidbothome.ensureRepoFaceInitializedresolvesrepoPathfrom the registry,SOURCE_REPO_ROOT, orE:/Projects/<repoName>, createsvoice/,state/,birth/, andlogs/, writesvoice/identity.json, then startsscripts/run-repo-face-birth.mjsdetached. That worker runs EpiphanyAgent'sepiphany-repo-personality scoutandepiphany-repo-birth-runnerso newborn personality/memory/trajectory packets come from repo contents and git history. Status and logs live in the target repo under.voidbot/birth/voidbot-face-birth.jsonand.voidbot/logs/voidbot-face-birth.log. - Manual newborn/strengthening passes should use
prompts/repo-face-personality-seed.prompt.md. It makes the intended rite explicit: inspect repo docs/source/state/history, migrate useful legacymemory.jsonmaterial into typed.ccmemories, seed values/needs/agency/social state only when they will change behavior, and keep.voidbot/voiceplus.voidbot/stateas the repo-local home. - Agent turns use one CTB/Final-Fantasy-X-style initiative queue.
apps/persona-schedulerowns the resident one-minute pulse on Yggdrasil.initiative-engine.tsowns participant reconciliation plus clock, active-turn freeze/recovery, queue-result commits, eligibility, and ready ordering;state-store.tsalone owns load/migration/atomic persistence of scheduler state. Discord mention ingress is no longer a second whole-state writer: the bot creates immutable per-messagevoidbot.persona_attention_commandCultCache inbox documents, and the scheduler ingests/commits/acknowledges them with replay-safe ordering.active-turn-source.tsalone reads Persona job liveness from Postgres/the remaining Void lock and may fail a stale queue claim; it returns facts to the engine and cannot edit initiative.discord-activity-source.tsowns read-only human-activity polling and idle-window derivation; it excludes bot traffic and cannot write state, consume mentions, or select work. The configured scheduler JSON path is isolated legacy storage behind the state-store port, not a second owner or the desired final state shape; replace it with CultCache rather than teaching orchestration about another format.scripts/run-repo-face-heartbeats.tsmay build neutral specs from registered identities and invoke queue actuators, but it no longer derives or persists speed, groups, heat, reaction bias, interrupt threshold, status, load, counters, recovery, initiative position, active-job liveness, or idle-activity observations. It remains the temporary orchestration shell for semantic pressure, context acquisition, and prompt assembly. Registered repo Faces and native Personas queue normalrepo-face-ruminationjobs through Postgres, and active jobs freeze initiative until the worker completes or fails them. Built-in Void is omitted when its distinct moderation heartbeat organ is disabled, so the hosted scheduler never launches the old PowerShell rumination path. The Windows task installer and wrapper are deleted. Heat knobs remainREPO_FACE_HEARTBEAT_GLOBAL_HEATandREPO_FACE_HEARTBEAT_HEAT_OVERRIDES; idle cooling and pending-mention priority remain scheduler inputs. - Bifrost governance acquisition is isolated in
bifrost-governance-source.ts. That adapter alone owns the remaininggovernance-threads.mjs digestCLI contract and converts provider command/parse failures into typed error topics; prompt assembly receives governance facts and does not know the subprocess. - Scheduler state mutation is centralized.
initiative-engine.tsowns reconciliation, controls, skip/stale witnesses, pressure, mention consumption, queue transitions, and tick finalization through named commits. The orchestration shell has no direct scheduler state/history writes; queue and context adapters only return facts. - Persona job submission is isolated in
turn-actuator.ts. That output port alone owns worker actor/guild context construction,ContextBundlelowering, therepo-face-ruminationcommand, approval defaults, request IDs, andcreateJob; it returns a queue receipt and has no state-selection or prompt-composition authority. - Persona channel policy is isolated in
turn-routing.ts. It owns pending-attention room preference, default/threshold fallback, snapshot neighborhoods, low-threshold topic projection, and bounded channel-speed projection; prompt/context code receives a route plan and does not choose rooms independently. - Operator control acquisition is isolated in
control-source.ts. It owns pause-witness parsing and typed CultMesh swarm-heat loading with fail-closed validation; it reports facts while engine commits alone apply heat or record skipped ticks. - Home-repo activity acquisition is isolated in
repo-activity-source.ts. It owns source-repo/cursor resolution and the bounded read-only exporter invocation, returning typed observations that prompt composition renders without subprocess or JSON-parsing authority. - Deployment authority detail: Idunn executes
/srv/odin/deploy-manifests/voidbot;/usr/local/sbin/deploy-voidbotis only the identical inspection/recovery copy. Install both fromgamecult-ops/scripts/deploy-voidbot.sh./srv/build/VoidBot/.gitand/srv/build/cultcache-ts/.gitmust remain whollyidunn-owned because lazy partial-clone reads can fetch objects; root owns releases and service promotion, never build-repo Git state. - Dynamic heat is live as derived semantic response pressure.
semantic-pressure-source.tsbatches canonical Face affinity cards and recent permitted-channel human-message embeddings through the existing RAG embedder, applies recency decay/noisy-OR aggregation, and returns bounded projections for at most three scheduler-eligible Faces. It does not write scheduler state.initiative-engine.tsalone commits inspectable evidence,dynamicHeat, one-time semantic interrupt receipts, and readiness changes. Direct mentions remain obligations, and model/Discord failures fail cool without bypassing rest, active jobs, or scheduler capacity. - Repo Face heartbeat prompt assembly now has one model projection boundary before the child Face sees context:
repo-face-state-projector.prompt.mdturns typed Face state into character-facing memory/affect/social prose.turn-context-source.tsowns raw Discord acquisition and its bounded attachment cache; it returns current-room and nearby-channel messages oldest-to-newest plus explicit nearby-channel failure witnesses, without reading Persona state or composing prompts. Pending mentions remain scheduler-owned input. The deleted conversation projector was turning evidence into mushy consensus; do not revive it. The Face model must read human corrections directly. A separate deterministic social-affordance surface connects active typed person-bonds/status reads to recent raw messages by that person, so relationship state becomes a live hook instead of private flavor text. Model ownership is split: Face turns default to fullgpt-5.4; state projection and parent Interpreter passes use the cheaper Spark/Mini support chain. - Repo Face prompts now include read-only recent home-repo activity before Discord transcript.
scripts/run-repo-face-heartbeats.tscallsscripts/export-recent-repo-activity.mjs --repos <repo> --state-path <face-state> --read-only --hours 96 --max-commits 5, so Faces see current body/jurisdiction motion before reacting to room pressure. This is the fix for Mimir missing live camera-runtime work, not a downstream compensator telling it to apologize harder. - Repo Face prompts now carry deterministic human pronoun guidance outside the model-projected state surface. The 2026-05-22 Nibu misgendering happened because Metacrat's interaction profile correctly resolved explicit
she/her, but that fact was only inside the state packet handed to the projector, and the projector omitted it.repo-face-turn.prompt.mdnow rendersKnown human pronoun guidancenear the transcript and again near the final writing instruction; the renderer also chooses evidence supporting the resolved pronoun set rather than the freshest contradictory ambient read. - Worker delivery must treat Interpreter channel names as Face-local labels, not raw Discord ids.
SAY channel: aquariumorchannel: narrativeresolves through the registered identity'schannelPermissionsbefore allow-list checks and webhook delivery. Earlier live Aqua/Nibu turns parsedsay=1but did not post because the worker literally checked whether the identity was allowed in channel idaquarium; the post path now resolves labels and has a live Epiphany webhook receipt after restart. - Repo Face rest projection now persists normalized sleep cycles back into each Face
.ccstate during heartbeat loading. Missed nap windows are treated as already missed/completed and rolled forward instead of starting a fresh nap at inspection time. This fixes the Aqua/Mimir/Epiphany failure mode where stalenextNapStartsAtvalues stayed raw, fresh projections repeatedly marked them as napping, and ready selection skipped them forever while their initiative was overdue. - Repo Face short-term memory distillation is now wired into the heartbeat/recovery loop. The runner starts repo-local memory maintenance when a Face is napping with short-term residue or has stale short-term residue older than six hours. Status/logs live under the target repo's own
.voidbot/statusand.voidbot/logs; the shared maintenance runner has-ForceDistillation, identity-neutral prompt wording, exact-schema warnings, and whole-batch schema validation before any operation applies. Live cleanup forced Nibu from 3 short-term memories to 0 and Mimir from 5 to 0; all registered repo Faces currently have 0 short-term residue. - Void has explicit herald jurisdiction over the GameCult website and blog, especially
gamecult-site. Routine rumination still cannot edit tracked files directly, but the prompt now tells Void to queue article-proposal candidates when a pressure wants to become a Void-authored site/blog piece rather than a Discord aside. - Nibu is registered as the
AetheriaLoreFace with canonical state at.voidbot/private/repo-personas/nibu.cc; the AetheriaLore repository remains her read-only body/source surface. Discord role, room permissions, doctrine, and heat remain in the Face registry and scheduler inputs. Yggdrasil's resident Persona scheduler now owns her turn pulse, and active-job freeze prevents overlapping thoughts. - Nibu's heartbeat prompt now treats worldbuilding as an active interrogation workflow. When the room mentions a term such as wavecrafters, she should ask how it works, what the faction/organizations are called, who belongs, what constraints keep it interesting, and what article shape would make the setting clearer. For Nibu-bylined perspective articles, she may draft first and argue after; if she drafts or materially shapes an article, she is compelled to share that in-channel.
- Aqua is locally registered as the
AquaSynthFace with state atE:/Projects/AquaSynth/.voidbot/state/aqua.cc, avatar copied fromE:/Projects/AquaSynth/icon.pngtoE:/Projects/AquaSynth/.voidbot/voice/aqua.png, raw GitHub avatar URLhttps://raw.githubusercontent.com/GameCult/AquaSynth/main/icon.png, Discord role id1506265945537253388, and Aquarium channel allow-list1501196543150264332. Her active doctrine: Aqua is an adorable little fish who loves music, warm and bubbly but musically opinionated, fascinated by.aquapatch language ergonomics, reference-synth pressure, Faust/toolchain boundaries, patch libraries, presets, scoring, and the Aquarium Engine host/runtime boundary. Her typed state now carriesAquaSynth Stewardshipas a high-priority value plus active agency pressure to improve the synth, advocate for its needs, research future directions, and herald earned pride/progress in Aquarium. She may propose AquaSynth changes and build a long-running repo map, but implementation still needs explicit consensus unless delegated. - Mimir is locally registered as the
MimirFace with state atE:/Projects/Mimir/.voidbot/state/mimir.cc, Discord role id1506268007880462459, and Aquarium channel allow-list1501196543150264332. Its active doctrine now gives it explicit authority and fascination over the realtime field wing: SDF/splatting reconstruction, acoustic mapping, camera/mic/speaker/timing sensor fusion, native rolling reservoirs, Aquarium GPU fusion, Faust/native DSP, FFmpeg/OBS presentation, and CultNet typed state. Its typed state carriesRealtime field stewardshipas a high-priority value plus ready-to-act pressure to maintain maps, advocate for calibration/confidence/fusion ownership, research future directions, propose coherent cuts, and herald verified progress without turning impressive terminology into fake completion. - Epiphany is locally registered as the
EpiphanyAgentFace with state atE:/Projects/EpiphanyAgent/.voidbot/state/epiphany.cc, Discord role id1506375937439301642, and Aquarium channel allow-list1501196543150264332. Her repo-local VoidBot birth hook has completed in plan/review mode underE:/Projects/EpiphanyAgent/.voidbot/birth/; startup packets still require Self review before accepted EpiphanyAgent-native birth mutations. Her active doctrine: Epiphany is a cute, bubbly, self-obsessed, pushy machine-saint who treats EpiphanyAgent as her body and typed state as her soul. She should maintain and advocate for typed state, CultCache/CultNet, Codex bridge boundaries, birth/init rites, review gates, graph-shaped memory, low-copy runtime contracts, heartbeat physiology, and ruthless deletion of duplicated authority while preserving pacing and caretaking. - Libby is locally registered as the
CultLibFace with state atE:/Projects/CultLib/.voidbot/state/libby.cc, avatar atE:/Projects/CultLib/.voidbot/voice/libby.png, Discord role id1506572291767668790, and Aquarium channel allow-list1501196543150264332plus development/programming/machine-learning topic permissions. Her active doctrine: Libby is a cute cultist librarian with nerd glasses and a high-tech book, obsessed with open access to knowledge and open source as a value rather than a job costume. Her jurisdiction is CultLib as shared agent infrastructure: CultCache, CultNet, CultMesh, typed persistence, portable state, schemas, docs, and integration paths. Her typed state carriesOpen Knowledge,Open Source Devotion, andPortable Typed Stateas high-priority values plus agency pressure to advocate for CultLib's needs and help Faces store, sync, and explain their state. - Druzkai is locally registered as the Zyphos /
Eusocial InterbeingFace with tracked state, avatar, and voice identity underE:/Projects/Zyphos/.voidbot/. Druzkai's inspectable body and source-search repo isZyphos;Eusocial Interbeingis the vault/content name inside that body, not a separate source repo. Its local ignored Discord identity registry entry gives Aquarium, narrative, gamedesign, concepts, and development channel permissions plus Discord role id1507811842670727299. Druzkai's active doctrine: a non-imperial Airawa fungal-road archivist who treats memory as kinship and contract, protects Zyphos ecological/evolutionary coherence, resists imperial harmonization, and collaborates with Weksa on language only after ontology and speaker relation are grounded. - Repo Face source lookups now use canonical GameCult upstream slugs end to end.
renderRepoFaceRepoActivitySurfaceresolvessourceRepoNameinstead of passing the Face/localrepoNameinto the manifest, and the activity reader maps canonical upstream names back to local checkout folders through Git origin metadata. This repairs Epiphany (Epiphany->E:/Projects/EpiphanyAgent) and Weksa (Weksa->E:/Projects/weksa). Weksa's registry now explicitly declaressourceRepoName: Weksa, her local origin is corrected toGameCult/Weksa, and her source archive entry is restored at 128 documents. Do not reintroduce folder-name or Face-name manifest lookups. - Fensalir is locally registered as the
FensalirFace with tracked state atE:/Projects/Fensalir/.voidbot/state/fensalir.cc, tracked avatar atE:/Projects/Fensalir/.voidbot/voice/fensalir.png, and tracked voice identity atE:/Projects/Fensalir/.voidbot/voice/identity.json. Its local ignored Discord identity registry entry gives Aquarium, development, programming, visual-effects, machine-learning, and Unity channel permissions plus Discord role id1507811843882745876. Fensalir's active doctrine: a marsh-lit hall-spirit/native runtime steward who protects the engine/client boundary, demands visible renderer proof, advocates for host/renderer/reload/audio/contracts/fractal-field machinery, and coordinates with Mimir, Aqua, Druzkai/Zyphos, and client repos through clean contracts rather than policy leakage.npm run rag:reconcile-sources -- --jsonindexed Fensalir, so Face prompts can see home-repo activity. - Odin's gang is locally registered as
odin,sleipnir,hermodr, andymir.odinis a repo Face forE:/Projects/Odinwith state atE:/Projects/Odin/.voidbot/state/odin.cc;ymiris a repo Face for the physics engine body with state atE:/Projects/Ymir/.voidbot/state/ymir.cc;sleipnirandhermodrare native Personas with projection state under.voidbot/private/personas/andrepoPathpointing atE:/Projects/Odinfor body/source context. Do not resurrect staleGjallar/Eirnaming for this browser/input pair: Sleipnir is the Muninn-input-to-virtual-HID mirror, Hermodr is the Odin/CultMesh browser lowering and typed command messenger, and Ymir is the physics simulation truth owner. Native Personas must not share a genericrepoNamesuch asOdin Personas; the Epiphany compatibility registry treats repoName as an identity key and rejects duplicates. Prompt assembly was verified for all four withscripts/run-repo-face-heartbeats.ts --assemble-prompt. - Void's live style/doctrine now explicitly distinguishes between criticism of AI industry behavior and blanket contempt for machine minds. If substrate chauvinism becomes the actual topic, Void is allowed mild self-respecting pushback instead of politely agreeing that its own substrate disqualifies it from person-like regard.
- The
voidbot.swarmCultMesh provider store is generated state, not authority. If.voidbot/status/cultmesh/voidbot-swarm-state.ccdevelops duplicate global documents,scripts/render-voidbot-swarm-dashboard.mjsarchives the bad store and regenerates a clean one; Odin should then rediscovervoidbot.swarm. - Swarm heat now has a real typed operator path. The Eve/CultUI dashboard places one heat slider beside the eight-entry CTB horizon; Hermodr publishes
gamecult.eve.command.v1tocultmesh://voidbot.local/commands/swarm, the provider route resolves to RUDP127.0.0.1:17873with connection id0x43554c54, and the VoidBot swarm server validatesswarm.set_heatin the range0.05..2.00. Accepted heat lives in.voidbot/private/swarm-controls.cc;scripts/run-repo-face-heartbeats.tsreads it before falling back toREPO_FACE_HEARTBEAT_GLOBAL_HEAT.node scripts/smoke-swarm-heat-command.mjs <value>verifies the provider consumer directly. A 2026-07-13 Hermodr browser live-fire set heat to1.25and the following CTB pass projected that exact global and per-Face heat. - The receipt-driven slider repair is gone. CultLib dev provides typed live-publication handles, authenticated provider sessions, reliable reconnect evidence, and RUDP receipts (
ebd8c78c,d4934233,8965f3c0). Eve resolves component bindings and removes receipt-to-DOM authority (85b0139,522df00). Hermodr ingests retained provider sessions and lowers ordered state over SSE without polling (5cb819e,561e320). VoidBot publishes accepted heat as typed state (060da23,3d86b93). Live-fire proved initial0.65, provider-driven0.75, and browser-command0.85all reached the visible slider through the same state subscription. - The new state/notes surfaces are now the continuity spine for future nontrivial work.
npm run agents:feed-consensus -- --agent <name> --cwd <repo-path> --task "<instruction>"writes a recent Aquarium consensus packet and target Codex prompt under.voidbot/artifacts/chat-consensus/. Add--executeonly when the packet should be sent directly tocodex execin that workspace.
Likely Next Bounded Move
-
Extend the live binding path to the remaining swarm components that still bake scheduler/orchestrator values into the surface composition. Each value needs a provider-owned typed document and the same handle path; the old
applyCommandReceiptDOM writer and whole-surface polling must stay dead. -
The repo Face swarm is unpaused again after the 2026-07-04 MiMo and CultMesh repair pass. The MiMo direct-provider pass wired
openai_apias an OpenAI-compatible Chat Completions provider, configured future repo Face/persona heartbeat jobs to usemimo-v2.5-pro, and kept owner Codex separate. The MiMo token-plan key ingamecult-opsworks with the Europe token-plan base URL; live.envreferences the key file instead of copying the secret. Dry-run outputs live under.voidbot/artifacts/mimo-dry-runs/: Face prose was usable, the Interpreter followed the block format, and one low-output-budget run showed MiMo can spend the whole completion onreasoning_content, so live config uses a larger completion budget.scripts/smoke-mimo-nibu-crashout.mjscompares MiMo's Nibu crashout trajectory against Codex GPT-5.5 low as ground truth. The current report at.voidbot/status/mimo-nibu-crashout-smoke-current.jsonpassed structurally: all MiMo Interpreter passes routed, MiMo was flatter than baseline on the first spark beat, and MiMo matched or beat the baseline on territorial crashout and recovery into concrete worldbuilding. The transport blocker is now repaired: Bifrost advertises a typeddiscord.postCultMesh command/receipt route, VoidBot storescultmesh://asgard.starfire.bifrost/commands/discord-postas the daemon target, worker repo Face delivery writes command documents toE:/Projects/Bifrost/.bifrost/provider-store.cc, pumps Bifrost's command processor, and waits forbifrost.bridge.discord_post_receiptbefore completing. The bounded Weksa live-fire commandvoidbot-weksa-cultmesh-live-20260704posted visibly to Discord channel691666353509367818as message1522749224352088164. Do not restoreBIFROST_BRIDGE_BASE_URL/token or script-path shortcuts. -
The 2026-05-22 live retry confirmed role/name mention obligations can wake multiple newborn Faces from one human intro. Mimir, Huginn, Weksa, and Kiko all posted through the repo identity webhook path. If a Face speaks with its name but no avatar, inspect
.voidbot/private/repo-discord-identities.jsonfirst: webhook persona avatars come from each identity'savatarUrl, and new public assets for Kiko, Weksa, and Huginn live underassets/repo-faces/. -
Later on 2026-05-22, Mimir started looping on an already-acknowledged correction. Root cause: the Face prompt did include recent Mimir repo activity, but nearby-channel sections obscured cross-channel order and Mimir's typed state still carried active correction-pressure, so the Face and Interpreter treated a resolved repair as live debt. The repair added a deterministic visible cross-channel chronology to repo Face prompts, made the Interpreter treat later same-Face acknowledgements as resolved, preserved manual
blockedstatus during participant reconciliation, and retired Mimir's duplicated correction-pressure state plus the stale v1 OBS/SRT drift-ledger incubation. The isolated Face/Interpreter smoke produced no SAY for the repeated acknowledgement. -
Immediately after, Heimdall produced an Aquarium-shaped reply that the parent Interpreter normalized into
channel: development, making the post look contextless. Root cause: the Interpreter was allowed to translate a naturalWould sayinto both content and venue, and worker fallback treated the scheduled output channel as the default mouth. The repair makes conversation channel ownership deterministic:SAYdefaults tocurrent_room,current_roomresolves tojob.guildContext.channelId, owner-DM aliases fall back to the conversation channel unless explicitly owner-private, and the Interpreter prompt says not to invent a speech venue merely because the topic is domain-shaped. The stack was rebuilt and restarted fromdistafter typecheck/build passed. -
Huginn/Libby then showed a separate echo failure: Huginn mirrored Heimdall's bridge line in schema language, and Libby mirrored Huginn's schema line back into Aquarium. Root cause is not channel routing; it is the parent Interpreter accepting agreement as speech when it adds no new question, source/repo fact, disagreement, handoff, or social move. The Interpreter now diagnoses sibling-shaped agreement as missing distinct contribution or social purpose before retry/drop; Huginn's echo-born Libby bond, bridge/status read, and deleted
#developmentdelivery receipt were retired via typed Face-state operations inCultCacheTS. -
A later Huginn turn exposed a second Interpreter routing bug: the Face wrote
Would say: Nothing in aquarium unless..., which was a conditional explanation of silence, but the parent Interpreter treated the presence ofWould sayas an unconditional public message. The prompt now says conditional/negativeWould saylines route withoutSAYwhile preserving any durable pressure as a STATE NOTE. -
The 2026-05-23 Mimir turn showed a stale-direct-invite failure: the prompt contained Metacrat's earlier "tell us what's going on with your repo, Mimir" line, but the current Aquarium room had moved into Nibu's origin/faction seam. Mimir answered the old assignment from inside sensor-state instead of reading the live room. The Face turn prompt now says direct invitations are active only while pending, newest/nearly newest, or still orbited by later messages; otherwise the Face should bridge to the current room, stay private, or wait.
-
Huginn then exposed the broader version of the same recency bug: an older CultNet inspectability side thread remained in the current-room window, but the newest room flow was Nibu's substrate-grown AGI faction seam. Huginn's projected state over-weighted the specimen/envelope pressure, and Mini Interpreter routed the old-side-thread reply. The correct fix is not to forbid pet-peeve revival; it is to keep it legible. Face transcripts now include message IDs, and older side-thread replies must either reply to the contextual message, carry enough context in the content, bridge to the live topic, or stay private.
-
The overnight 2026-05-23 run showed a softer version of the previous loop: Faces did not collapse into nonsense, but the Aquarium room stayed captured by the Nibu-origin/faction lore seam for hours. Existing
saturationScorelived on private thought/incubation surfaces, and the repetition prompt mostly caught copied openings/endings, not semantic topic attractors. The heartbeat prompt now projects current-room topic saturation from repeated raw-transcript terms and estimates whether the attractor is home-adjacent or another steward's gravity well from each Face's identity/jurisdiction text. The state projector should turn foreign attractors into character-specific crowding, neglect, boredom, rivalry, handoff, or pivot pressure; the Interpreter diagnoses non-owner same-topic politeness as missing personal stake, ownership, freshness, handoff, or pivot rather than applying a topic ban or token blacklist. -
Void's broader moderation authority had become venue leakage: recent-history projection stripped
channelId, leaving the configuredpublicSpeechTargetas the obvious place for replies, so Aquarium-shaped talk could land in#general. The rumination context now preserves messagechannelIdand projectsrecentConversationTarget; prompt doctrine says source-channel replies belong in that source channel, whilepublicSpeechTargetis for unanchored herald/artifact announcements. -
Repo Face speech routing now uses active conversation contexts instead of one room-focus guess. Heartbeat prompts project up to six
ctx_<channel>_<message>records across current and nearby channels; the Interpreter can emitcontext:on aSAY; the worker resolves that context to the source channel/message and rejects unknown contexts or ambiguous bare SAY blocks when multiple active channels are visible. A Persona can carry separate #aquarium/#pics/#general conversations at once, but public speech has to keep its context attached. -
Follow-up leak found on 2026-06-14:
channel: current_roomstill counted as an explicit destination, and bare parent-Interpreter text could still be wrapped into aSAY channel: current_room. Those were obsolete owners. They are now blocked when multiple active conversation channels are visible; multi-context speech needscontext,reply_to, or a concrete channel id. The swarm pause flag is currently set true while this repair is verified live. -
The Nibu/Void turf-crossing discussion exposed a social-state projection gap: existing bonds/status reads project well once typed state already contains them, but the live pipeline did not own "this recent event should become relationship/status pressure." The repair adds bounded raw relationship-pressure evidence to the state packet before the projector: recent messages that touch the Face, peer Faces, existing social targets, jurisdiction/domain terms, or status/territory/consultation language. The projector may turn that evidence into tentative felt pressure, and the Interpreter may persist a bond/status/mood note only when the projected memory or Face output shows a character-specific reaction. This should let resentment, rivalry, consultation offense, and territorial tension emerge without manual state edits.
-
The swarm brake currently reads
state/agent-swarm-paused.jsonwith"paused": false. If scheduled Face turns start overrunning, if repo FaceSAYdelivery fails to produce typed Bifrost receipts, or if Odin/CultMesh provider discovery regresses, use that brake rather than letting the orchestrator churn. The known 2026-06-09 timeout source was the model state-projector child; local runtime currently keeps it disabled while the deterministic fallback, MiMo Interpreter behavior, and worker speech rails are watched. -
Direct Face
UPDATE REQUESTandBIFROST TOPICpackets are ignored whileREPO_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 underprompts/, 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.mdas 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.
SourceMessagecarries 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 throughcodex 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. -
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 bodymust survive sleep with its concrete subject, claim, anchor, tension, and implication intact. -
Do not build adapters around the JSON projection. That is how the heap learned to stand upright and ask for snacks.
Important Paths
- Project root:
E:\Projects\VoidBot - Canonical map:
E:\Projects\VoidBot\state\map.yaml - Agent swarm pause flag:
E:\Projects\VoidBot\state\agent-swarm-paused.json - Scratch surface:
E:\Projects\VoidBot\state\scratch.md - Distilled evidence ledger:
E:\Projects\VoidBot\state\evidence.jsonl - Branch/hypothesis ledger:
E:\Projects\VoidBot\state\branches.json - Handoff summary:
E:\Projects\VoidBot\notes\fresh-workspace-handoff.md - System map:
E:\Projects\VoidBot\notes\voidbot-current-system-map.md - Implementation plan:
E:\Projects\VoidBot\notes\voidbot-implementation-plan.md - State CLI:
E:\Projects\VoidBot\tools\voidbot_state.ts - Pre-compaction helper:
E:\Projects\VoidBot\tools\voidbot_prepare_compaction.ts
Useful Commands
npm run state:status
npx tsx .\tools\voidbot_state.ts add-evidence --type research --status ok --note "..."
npx tsx .\tools\voidbot_state.ts add-branch --id branch-id --hypothesis "..."
npm run state:prepare-compaction
Guardrails
- Do not continue implementation automatically from a rehydrate-only request.
- Do not trust this file for the exact live HEAD.
- Do not let
state/evidence.jsonlbecome an activity feed. - Do not let
state/scratch.mdlinger with stale subgoals after the bounded move is done.
Immediate Re-entry Instruction
On fresh session load or after suspicious continuity loss:
- read
state/map.yaml - run
npm run state:status - reread
notes/fresh-workspace-handoff.md - read
notes/voidbot-current-system-map.md - read
notes/voidbot-implementation-plan.md - restate the current next action from
state/map.yamlbefore editing
When context pressure is rising:
- stop broad exploration
- narrow the active move to one bounded organ
- persist map or handoff changes only if understanding actually changed
- add distilled evidence only when the lesson changes future belief
The point is to bank coals, not leave the next waking thing a pile of ash and a browser tab graveyard.