VoidBot/notes/fresh-workspace-handoff.md
2026-07-15 23:51:53 +01:00

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:

  1. state/map.yaml
  2. notes/fresh-workspace-handoff.md
  3. notes/voidbot-current-system-map.md
  4. notes/voidbot-implementation-plan.md
  5. state/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 main and 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 upstream main, then verify Idunn converged DEPLOYED_REVISION and 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-scheduler inside voidbot.service owns the serialized Persona pulse and queues turns through Postgres for the worker. The Windows GameCult Local Orchestrator no 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": 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.
  • 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_codex and local_llm are 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 .cc extension. 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, old void.persona_affect, and affect stressResponses may be read during migration, but the live repair/write path promotes affect to void.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, and update_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. loadVoidSelfState is a thin loader that reads typed .cc documents 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.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.
  • scripts/run-void-moderator-rumination.ps1 has been rebuilt as a typed rumination runner. It builds a typed context packet, loads prompts/void-moderator-rumination.md, runs Codex rumination, and applies only the JSON operation payloads that come back through scripts/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 like 5 minutes ago, including recent Discord messages, repo activity, receipts, memory timestamps, and sleep/speech timing.
  • scripts/simulate-void-mood.mjs owns typed sleep/speaking-pressure over .voidbot/private/void-self-state.cc. During sleep, it invokes typed memory maintenance once per nap unless that nap already completed a pass; use --skip-memory-maintenance for smoke runs that must not call Codex.
  • npm run moderation:memory-maintenance:sleep-fixture now proves the non-skip sleep path without spending a live model call: it seeds a temporary .cc, forces mood drift through sleep maintenance, uses a fake Codex child to propose apply_memory_distillation, and verifies short-term residue is gone while the durable memory keeps subject, claim, tension, action implication, and anchor.
  • 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.
  • 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. 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 THREAD action 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:fixture proves the non-skip typed rumination branch without spending a live model call: it seeds a temporary .cc, routes the runner through a fake Codex child, and verifies short-term memory, incubation, agency pressure, and candidate intervention proposals are parent-applied typed operations with no exact timestamp leakage in the prompt-facing context.
  • npm run moderation:rumination:speech-fixture proves 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 one apply_memory_distillation operation, and left thoughtMemory.shortTerm empty 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-fixture proves 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.ps1 is the first replacement memory-maintenance organ. It builds a bounded context from typed memories/incubation/candidates/runtime, loads prompts/void-memory-maintenance.md, and applies only allowed memory/incubation/candidate typed operation proposals through scripts/void-self-state.mjs. In sleep mode, memory pressure sets sleepDirective.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 with apply_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_memory operation 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:fixture proves the guard.
  • Long-term memory is durable, not immutable. revise_durable_memory, retire_durable_memory, and crystallize_memory_into_identity make memory reconsolidation explicit; npm run moderation:memory-lifecycle:fixture proves 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_pressure own 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:fixture verifies 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 speechPressureObligations once 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-fixture covers the silent, spoken-history, and queued-candidate paths.
  • Severe safety evidence now has the same no-silent-advance wall. The moderation heartbeat projects urgentModerationWitnesses from 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-fixture still covers the older rumination guard, while npm run moderation:heartbeat:policy-fixture proves the heartbeat-only policy path.
  • Urgent safety handling can now enforce through a parent-owned Discord action path. VOID_MODERATION_ENFORCEMENT_MODE=policy enables instant bans and three-strike bans according to the explicit table in config/discord-server-rules.md; the child only opens typed cases with infringement:<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.ps1 now 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.mjs still skips sleep-cycle mutation and memory maintenance under a recent moderation lock, but it now reloads typed state and applies update_speaking_pressure first, so fresh canonical speech receipts update lastSpokeAt, damping, and needToSpeak even 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 with apply_memory_distillation or drops residue with prune_short_term_memories. Memory writes require a concrete target, claim or question, tension, action implication, and anchors unless the operation explicitly tags anchor: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.ts while prompt/rendering, tool-loop helpers, and shared types/constants live under packages/providers/src/local-llm-*.ts.
  • The Discord bot lane is no longer one swollen entrypoint; gateway wiring remains in apps/bot/src/discord-bot.ts while command/prompt handlers and Discord-shape support helpers now live under apps/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.ts while context bootstrap, resource registration, tool registration, Discord notify helpers, and shared formatting/schema helpers live under apps/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=false local Ollama sidecar in packages/core/src/situational-social-read.ts instead 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.ts is 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.ts now keeps a small manifest at .voidbot/rag/source-documents.json and 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:start and through the watchdog path, so new repos under SOURCE_REPO_ROOT stop waiting for manual hook folklore before they get indexed.
  • Bot-directed prompts now stay in the raw Discord archive but are excluded from default semantic history indexing and lexical fallback, so repeated summons stop teaching the bot its own call-and-response loops.
  • Source-grounding hints are advisory now; the owner and local lanes can decide when repo or lore tool use is actually warranted instead of obeying a lexical front-door veto.
  • The moderation heartbeat organ is invoked every orchestrator pulse against .voidbot/private/void-self-state.cc with prompts/void-moderation-heartbeat.md, which exposes only rules, recent history, urgent witnesses, and infringement history. The moderation rumination organ is still invoked by GameCult Local Orchestrator for Void's slower person-shaped participation and possible announcement turns; its prompt lives in prompts/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_cursor operations 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.contentHints exposes frontmatter/headings/excerpts, the prompt tells rumination to verify Void authorship through retrieval, and publicSpeechTarget gives 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_intervention operations 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 6 nostalgia 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 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.
  • 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-preoccupation and quiet-room-status are not durable thought kinds, person-singleton labels do not get to crystallize into doctrine, and boilerplate like looks like a live seam is 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 body must 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, but MessageCreate no longer falls straight into handlePrompt.
  • The moderation loop now speaks through node scripts/send-discord-message.mjs using 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 the post_discord_message MCP tool now also support a shared guild-channel webhook pipe with per-message personaName and optional personaAvatarUrl, 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_PATH maps 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 through post_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 is 113786069023064068; 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>.cc unless faceStatePath overrides it, ensures missing Discord roles on startup when DISCORD_GUILD_ID and permissions allow, and appends role/name-addressed prompts to pendingMentions in REPO_FACE_HEARTBEAT_STATE_PATH instead 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 next repo-face-rumination job, and consumes them only after a job is actually queued. Worker auto-posting stays suppressed for repo-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_info exposes the running MCP process id, workspace, self-state schema fingerprint, and registered document types. npm run agents:repo-face-affect-mcp-fixture -- --identity nibu and the same command for mimir spawn the built VoidBot MCP server, assert that runtime fingerprint includes void.face_affect, and call read_repo_face_state, proving typedState.faceAffect is readable through the tool surface that agents actually use. If the currently mounted Codex MCP tool still says No 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.ts recognizes current Epiphany Face turn prompts, labels them as repo Face turns, forbids generic HANDOFF_SENTINEL output 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 .cc self 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 say Void for 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 ARTICLE DSL 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 under Aetheria/Articles/ or non-Aetheria blog posts under GameCult/Blog/ in gamecult-site. Subreddit threads use REDDIT THREAD and Bifrost reddit-post with Persona flair. Legacy VOIDBOT_REPO_IDENTITY_ARTICLE JSON 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 restore BIFROST_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 TOPIC blocks, and the worker ignores BIFROST TOPIC / legacy UPDATE REQUEST packets unless REPO_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.ts builds a compact state packet, then calls the model prompt at prompts/repo-face-state-projector.prompt.md to turn it into character-facing prose before the Face sees it. REPO_FACE_STATE_PROJECTOR_ENABLED=false exists only as an emergency isolation switch; both the model output and fallback packet reject schema/prompt-construction leaks such as grants:, jurisdictions:, Face of, repo paths, and "do not prompt" phrasing.
  • First role-addressed chat also initializes the target repo's local .voidbot home. ensureRepoFaceInitialized resolves repoPath from the registry, SOURCE_REPO_ROOT, or E:/Projects/<repoName>, creates voice/, state/, birth/, and logs/, writes voice/identity.json, then starts scripts/run-repo-face-birth.mjs detached. That worker runs EpiphanyAgent's epiphany-repo-personality scout and epiphany-repo-birth-runner so 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.json and .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 legacy memory.json material into typed .cc memories, seed values/needs/agency/social state only when they will change behavior, and keep .voidbot/voice plus .voidbot/state as the repo-local home.
  • Agent turns use one CTB/Final-Fantasy-X-style initiative queue. apps/persona-scheduler owns the resident one-minute pulse on Yggdrasil. initiative-engine.ts owns participant reconciliation plus clock, active-turn freeze/recovery, queue-result commits, eligibility, and ready ordering; state-store.ts alone owns load/migration/atomic persistence of scheduler state. Discord mention ingress is no longer a second whole-state writer: the bot creates immutable per-message voidbot.persona_attention_command CultCache inbox documents, and the scheduler ingests/commits/acknowledges them with replay-safe ordering. active-turn-source.ts alone 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.ts owns 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.ts may 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 normal repo-face-rumination jobs 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 remain REPO_FACE_HEARTBEAT_GLOBAL_HEAT and REPO_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 remaining governance-threads.mjs digest CLI 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.ts owns 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, ContextBundle lowering, the repo-face-rumination command, approval defaults, request IDs, and createJob; 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-voidbot is only the identical inspection/recovery copy. Install both from gamecult-ops/scripts/deploy-voidbot.sh. /srv/build/VoidBot/.git and /srv/build/cultcache-ts/.git must remain wholly idunn-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.ts batches 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.ts alone 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.md turns typed Face state into character-facing memory/affect/social prose. turn-context-source.ts owns 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 full gpt-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.ts calls scripts/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.md now renders Known human pronoun guidance near 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: aquarium or channel: narrative resolves through the registered identity's channelPermissions before allow-list checks and webhook delivery. Earlier live Aqua/Nibu turns parsed say=1 but did not post because the worker literally checked whether the identity was allowed in channel id aquarium; 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 .cc state 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 stale nextNapStartsAt values 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/status and .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 AetheriaLore Face 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 AquaSynth Face with state at E:/Projects/AquaSynth/.voidbot/state/aqua.cc, avatar copied from E:/Projects/AquaSynth/icon.png to E:/Projects/AquaSynth/.voidbot/voice/aqua.png, raw GitHub avatar URL https://raw.githubusercontent.com/GameCult/AquaSynth/main/icon.png, Discord role id 1506265945537253388, and Aquarium channel allow-list 1501196543150264332. Her active doctrine: Aqua is an adorable little fish who loves music, warm and bubbly but musically opinionated, fascinated by .aqua patch language ergonomics, reference-synth pressure, Faust/toolchain boundaries, patch libraries, presets, scoring, and the Aquarium Engine host/runtime boundary. Her typed state now carries AquaSynth Stewardship as 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 Mimir Face with state at E:/Projects/Mimir/.voidbot/state/mimir.cc, Discord role id 1506268007880462459, and Aquarium channel allow-list 1501196543150264332. 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 carries Realtime field stewardship as 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 EpiphanyAgent Face with state at E:/Projects/EpiphanyAgent/.voidbot/state/epiphany.cc, Discord role id 1506375937439301642, and Aquarium channel allow-list 1501196543150264332. Her repo-local VoidBot birth hook has completed in plan/review mode under E:/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 CultLib Face with state at E:/Projects/CultLib/.voidbot/state/libby.cc, avatar at E:/Projects/CultLib/.voidbot/voice/libby.png, Discord role id 1506572291767668790, and Aquarium channel allow-list 1501196543150264332 plus 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 carries Open Knowledge, Open Source Devotion, and Portable Typed State as 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 Interbeing Face with tracked state, avatar, and voice identity under E:/Projects/Zyphos/.voidbot/. Druzkai's inspectable body and source-search repo is Zyphos; Eusocial Interbeing is 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 id 1507811842670727299. 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. renderRepoFaceRepoActivitySurface resolves sourceRepoName instead of passing the Face/local repoName into 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 declares sourceRepoName: Weksa, her local origin is corrected to GameCult/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 Fensalir Face with tracked state at E:/Projects/Fensalir/.voidbot/state/fensalir.cc, tracked avatar at E:/Projects/Fensalir/.voidbot/voice/fensalir.png, and tracked voice identity at E:/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 id 1507811843882745876. 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 -- --json indexed Fensalir, so Face prompts can see home-repo activity.
  • Odin's gang is locally registered as odin, sleipnir, hermodr, and ymir. odin is a repo Face for E:/Projects/Odin with state at E:/Projects/Odin/.voidbot/state/odin.cc; ymir is a repo Face for the physics engine body with state at E:/Projects/Ymir/.voidbot/state/ymir.cc; sleipnir and hermodr are native Personas with projection state under .voidbot/private/personas/ and repoPath pointing at E:/Projects/Odin for body/source context. Do not resurrect stale Gjallar/Eir naming 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 generic repoName such as Odin Personas; the Epiphany compatibility registry treats repoName as an identity key and rejects duplicates. Prompt assembly was verified for all four with scripts/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.swarm CultMesh provider store is generated state, not authority. If .voidbot/status/cultmesh/voidbot-swarm-state.cc develops duplicate global documents, scripts/render-voidbot-swarm-dashboard.mjs archives the bad store and regenerates a clean one; Odin should then rediscover voidbot.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.v1 to cultmesh://voidbot.local/commands/swarm, the provider route resolves to RUDP 127.0.0.1:17873 with connection id 0x43554c54, and the VoidBot swarm server validates swarm.set_heat in the range 0.05..2.00. Accepted heat lives in .voidbot/private/swarm-controls.cc; scripts/run-repo-face-heartbeats.ts reads it before falling back to REPO_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 to 1.25 and 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 initial 0.65, provider-driven 0.75, and browser-command 0.85 all 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 --execute only when the packet should be sent directly to codex exec in 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 applyCommandReceipt DOM 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_api as an OpenAI-compatible Chat Completions provider, configured future repo Face/persona heartbeat jobs to use mimo-v2.5-pro, and kept owner Codex separate. The MiMo token-plan key in gamecult-ops works with the Europe token-plan base URL; live .env references 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 on reasoning_content, so live config uses a larger completion budget. scripts/smoke-mimo-nibu-crashout.mjs compares 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.json passed 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 typed discord.post CultMesh command/receipt route, VoidBot stores cultmesh://asgard.starfire.bifrost/commands/discord-post as the daemon target, worker repo Face delivery writes command documents to E:/Projects/Bifrost/.bifrost/provider-store.cc, pumps Bifrost's command processor, and waits for bifrost.bridge.discord_post_receipt before completing. The bounded Weksa live-fire command voidbot-weksa-cultmesh-live-20260704 posted visibly to Discord channel 691666353509367818 as message 1522749224352088164. Do not restore BIFROST_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.json first: webhook persona avatars come from each identity's avatarUrl, and new public assets for Kiko, Weksa, and Huginn live under assets/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 blocked status 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 natural Would say into both content and venue, and worker fallback treated the scheduled output channel as the default mouth. The repair makes conversation channel ownership deterministic: SAY defaults to current_room, current_room resolves to job.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 from dist after 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 #development delivery receipt were retired via typed Face-state operations in CultCacheTS.

  • 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 of Would say as an unconditional public message. The prompt now says conditional/negative Would say lines route without SAY while 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 saturationScore lived 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 configured publicSpeechTarget as the obvious place for replies, so Aquarium-shaped talk could land in #general. The rumination context now preserves message channelId and projects recentConversationTarget; prompt doctrine says source-channel replies belong in that source channel, while publicSpeechTarget is 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 emit context: on a SAY; 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_room still counted as an explicit destination, and bare parent-Interpreter text could still be wrapped into a SAY channel: current_room. Those were obsolete owners. They are now blocked when multiple active conversation channels are visible; multi-context speech needs context, 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.json with "paused": false. If scheduled Face turns start overrunning, if repo Face SAY delivery 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 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.

  • 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.

  • 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.jsonl become an activity feed.
  • Do not let state/scratch.md linger with stale subgoals after the bounded move is done.

Immediate Re-entry Instruction

On fresh session load or after suspicious continuity loss:

  1. read state/map.yaml
  2. run npm run state:status
  3. reread notes/fresh-workspace-handoff.md
  4. read notes/voidbot-current-system-map.md
  5. read notes/voidbot-implementation-plan.md
  6. restate the current next action from state/map.yaml before editing

When context pressure is rising:

  1. stop broad exploration
  2. narrow the active move to one bounded organ
  3. persist map or handoff changes only if understanding actually changed
  4. 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.