62 KiB
62 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
- Discord bot and worker are live.
- The unattended repo Persona / CTB swarm is live again as of 2026-05-21 after the teardown/restart pass.
GameCult Local Orchestratoris enabled and owns the single hidden pulse for repo Persona CTB turns, Void mood drift, Void moderation rumination, and the watchdog. Bifrost dispatch is present but disabled by default behindBIFROST_DISPATCH_ENABLED=false. - The teardown authority remains
notes/agent-swarm-teardown-plan-2026-05-21.md, but it is now a restart checklist and smell ledger rather than a stop sign. Keep using it when a Persona/Bifrost/CTB boundary smells like prompt/parser/transport Jenga. - The repo-controlled pause flag is
state/agent-swarm-paused.json. While it is missing or has"paused": false, swarm runners may operate. While it has"paused": trueor is malformed, the Persona heartbeat runner skips and the orchestrator skips agent-swarm organs. This is the brake. Do not route around it. - Postgres owns jobs, audit events, interaction memory, and rate-limit state.
- 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. - 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.
- 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.- The live local pulse is now one hidden scheduled task:
GameCult Local Orchestrator. It invokes repo Persona CTB heartbeats, Void mood drift, Void moderation rumination, and the operations watchdog with one lock, one status surface, per-organ logs, and per-organ timeouts. The Bifrost dispatch organ remains wired but is skipped unlessBIFROST_DISPATCH_ENABLED=true. The old per-organ tasks are disabled and should remain disabled unless testing one organ in isolation. scripts/start-voidbot-stack.ps1now treats the local Bifrost container stack as a startup dependency. It readsBIFROST_ROOTandBIFROST_HEALTH_URL, runsE:\Projects\Bifrost\compose.local.yamlwhen the health endpoint is down, and records the result underruntime-stack.jsonasbifrost.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:bifrost-transport-fixtureproves queued speech and moderation status DMs cross the required Bifrost bridge path and return receipts to typed state.npm run moderation:rumination:speech-fixtureproves the parent-owned candidate closure path with a fake Bifrost bridge. The child prompt 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. - 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 then-current bot transport and recorded a typed speech receipt. Current unattended delivery should cross through Bifrost by default.
- 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 rumination organ is invoked by
GameCult Local Orchestratoragainst.voidbot/private/void-self-state.ccwithout reviving the legacy projection path. The prompt lives inprompts/void-moderator-rumination.md; treat that file as a sensitive behavior surface, 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 Persona mentions, human replies to a Persona's webhook/persona message, and ordinary Void mentions/DMs append pending obligations to
REPO_PERSONA_HEARTBEAT_STATE_PATH; the CTB scheduler pulls the addressed agent forward and passes the pending prompt/reply target into the next Persona 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 Persona instead of requiring the message to begin with one name. Mention detection can happen outside the regular autonomous Persona 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 crosses Discord through Bifrost only.
scripts/run-void-moderator-rumination.ps1callsBIFROST_ROOT/tools/bifrost-bridge.mjsfor public candidate posts and user moderation-status DMs, then converts the Bifrost receipt into typed Void state.VOID_DISCORD_TRANSPORT=directis rejected in this runner; direct VoidBot-native transport is no longer part of the moderation delivery authority. - 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 Persona 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 Personas 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 Persona heartbeat default. - Repo Personas now have topic/channel speech permissions. Aquarium is the deliberately low-threshold general-musing lane for all Personas; 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 Persona 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 Persona state now. The bot resolves each identity to
.voidbot/private/repo-personas/<identity>.ccunlesspersonaStatePathoverrides it, ensures missing Discord roles on startup whenDISCORD_GUILD_IDand permissions allow, and appends role/name-addressed prompts topendingMentionsinREPO_PERSONA_HEARTBEAT_STATE_PATHinstead of creating direct repo-identity jobs. The CTB heartbeat scheduler temporarily pulls Personas with pending mentions forward, attaches those prompts plus reply targets to the nextrepo-persona-ruminationjob, and consumes them only after a job is actually queued. Worker auto-posting stays suppressed forrepo-persona-rumination; the child must emit a parent-owned action block for public delivery. - Repo Persona prompts now include an identity doctrine block. Void's style pack stays as discipline, humor permission, and source-grounding habit, but repo Persona jobs explicitly say the registered Persona identity overrides Void as the speaking subject.
- Repo Persona embodiment is now doctrine, not local flavor. A Persona 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 Persona'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 Persona'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.
- Persona affect is now a first-class typed state surface under
void.face_affect, shared by Void and repo Personas. 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 Persona 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-persona-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.personaAffectis 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 Persona turns must stay out of the generic owner-reply trap.
packages/providers/src/owner-codex-render.tsrecognizes current Epiphany Persona turn prompts, labels them as repo Persona turns, forbids genericHANDOFF_SENTINELoutput for them, and tells the child to use Persona action rails instead of bailing when Persona-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 Persona identity is now seeded into the typed
.ccself profile during initialization and passed through repo Persona MCP state reads/writes. The renderer may still provide identity context, but raw state should not quietly sayVoidfor a non-Void Persona. - Repo Persona prompts now also grant advisory repo-change agency: Personas 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 Persona draft, review, article, and Discord speech lanes now cross through explicit parent-owned action blocks before Bifrost bridge 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, routes Aetheria articles underAetheria/Articles/, routes non-Aetheria blog posts underGameCult/Blog/ingamecult-site, and callsBIFROST_ROOT/tools/bifrost-bridge.mjsfor the draft PR plus persona announcement. 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. - Repo Persona Bifrost/governance routing is paused. Persona 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_PERSONA_BIFROST_ENABLED=true. This is deliberate: the live bridge made agents talk to terse governance summaries instead of people. - Repo Persona state projection is no longer allowed to be deterministic schema slurry.
scripts/run-repo-persona-heartbeats.tsbuilds a compact state packet, then calls the model prompt atprompts/repo-persona-state-projector.prompt.mdto turn it into character-facing prose before the Persona sees it.REPO_PERSONA_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:,Persona of, repo paths, and "do not prompt" phrasing. - First role-addressed chat also initializes the target repo's local
.voidbothome.ensureRepoPersonaInitializedresolvesrepoPathfrom the registry,SOURCE_REPO_ROOT, orE:/Projects/<repoName>, createsvoice/,state/,birth/, andlogs/, writesvoice/identity.json, then startsscripts/run-repo-persona-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-persona-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 heartbeats now use one CTB/Final-Fantasy-X-style initiative queue.
scripts/run-repo-persona-heartbeats.tsstill has its old filename, but it now reconciles built-in system agents plus registered repo Personas, stores the roster atREPO_PERSONA_HEARTBEAT_STATE_PATH, advances virtual initiative time independently of wall-clock due dates, and dispatches selected turns. Repo Personas queue normalrepo-persona-ruminationjobs; built-invoidstarts the existing moderation rumination runner as its turn. Active jobs/locks freeze initiative: recovery starts only after the owner-Codex job or Void moderation lock clears, and completion cannot grant another turn in the same pulse. The active-job scan now marks stale approved/running Persona jobs failed through the job queue and recordsstale_active_turn_recovered, so a dead worker claim cannot freeze one Persona forever.GameCult Local Orchestratoris the live hidden local scheduled task that ticks the scheduler;npm run state:install-repo-persona-heartbeat-taskremains an isolation/backcompat installer, not the normal pulse owner. Heat knobs areREPO_PERSONA_HEARTBEAT_GLOBAL_HEATandREPO_PERSONA_HEARTBEAT_HEAT_OVERRIDES; overrides can targetall,kind:<participantKind>,turn:<turnKind>,identity:<id>,repo:<repo>,display:<name>,channel:<id>, or bare identity/repo/display keys. Idle cooling is part of the same scheduler: if watched Discord channels have no recent human message, unprompted swarm turns are globally clamped to one dispatch per idle recovery window, defaulting to one every 30 minutes, while pending mentions still bypass the brake. Repo Persona prompts now include a jurisdiction-dive cadence so unattended heartbeats periodically inspect repo/history terrain with source tools and convert real findings into state, proposals, essay seeds, agency pressure, or speech. - Repo Persona heartbeat prompt assembly now has one model projection boundary before the child Persona sees context:
repo-persona-state-projector.prompt.mdturns typed Persona state into character-facing memory/affect/social prose. Recent room messages, nearby channel snapshots, and pending mentions are now deterministic raw transcript sections, oldest-to-newest, with explicit recency/correction rules. The deleted conversation projector was turning evidence into mushy consensus; do not revive it. The Persona 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: Persona turns default to fullgpt-5.4; state projection and parent Interpreter passes use the cheaper Spark/Mini support chain. - Repo Persona prompts now include read-only recent home-repo activity before Discord transcript.
scripts/run-repo-persona-heartbeats.tscallsscripts/export-recent-repo-activity.mjs --repos <repo> --state-path <persona-state> --read-only --hours 96 --max-commits 5, so Personas 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 Persona 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-persona-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 Persona-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 Persona rest projection now persists normalized sleep cycles back into each Persona
.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 Persona short-term memory distillation is now wired into the heartbeat/recovery loop. The runner starts repo-local memory maintenance when a Persona 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 Personas 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 locally registered as the
AetheriaLorePersona with state atE:/Projects/AetheriaLore/.voidbot/state/nibu.cc, avatar atE:/Projects/AetheriaLore/.voidbot/voice/nibu.png, Discord role id1505918221453103247, Aquarium channel allow-list1501196543150264332, plus topic permissions for narrative, gamedesign, concepts, and development. Her active doctrines are AetheriaLore-first map-building, a more abrasive character voice than Void, consensus-backed authority to propose canon/vault changes, stewardship ofAetheria/Lore/Nibu.md, permission to write Nibu-authored Aetheria lore essays without canon consensus, and special fascination with adjacent setting seams: embodied ship minds, junkyard abandonment, murderous autonomy, salvage, and save-scumming survival. Local.envhas repo Persona heartbeats enabled through theGameCult Local Orchestratorone-minute pulse, and local heat currently boostsidentity:nibuso she gets substantially more turns when no prior Nibu job is still active. Above-baseline heat bypasses repo Persona nap gating for live-room mode, but active-turn freeze still prevents overlapping thoughts. Her heartbeat jurisdiction-dive cadence is hotter than the generic Persona cadence, and the prompt now explicitly tells her to turn Aetheria's reset-loop/coercive-continuity horror into source-grounded opinions instead of private inert summaries. - 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
AquaSynthPersona 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
MimirPersona 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
EpiphanyAgentPersona 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
CultLibPersona 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 Personas store, sync, and explain their state. - Druzkai is locally registered as the
Eusocial Interbeing/ Zyphos Persona with tracked state atE:/Projects/Eusocial Interbeing/.voidbot/state/druzkai.cc, tracked avatar atE:/Projects/Eusocial Interbeing/.voidbot/voice/druzkai.png, and tracked voice identity atE:/Projects/Eusocial Interbeing/.voidbot/voice/identity.json. 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. - Fensalir is locally registered as the
FensalirPersona 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 Persona prompts can see home-repo activity. - 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 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
- Keep the swarm awake, but watch the logs with suspicion. The restart pass thawed
state/agent-swarm-paused.json, enabledGameCult Local Orchestrator, restarted a fresh bot/worker stack, and verified healthy watchdog/repo Persona pulses. The current patch removes repo Persona turns from the generic owner-reply wrapper, coerces stray owner-DM aliases back to the live room unless explicitly marked owner-private, pauses Bifrost/governance routing so agents return to open room discussion plus private state pressure, and routes volatile Persona state through the actual state projector model before child prompt assembly. - The 2026-05-22 live retry confirmed role/name mention obligations can wake multiple newborn Personas from one human intro. Mimir, Huginn, Weksa, and Kiko all posted through the repo identity webhook path. If a Persona 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-personas/. - Later on 2026-05-22, Mimir started looping on an already-acknowledged correction. Root cause: the Persona 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 Persona and Interpreter treated a resolved repair as live debt. The repair added a deterministic visible cross-channel chronology to repo Persona prompts, made the Interpreter treat later same-Persona 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 Persona/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 Persona-state operations inCultCacheTS. - A later Huginn turn exposed a second Interpreter routing bug: the Persona 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 Persona turn prompt now says direct invitations are active only while pending, newest/nearly newest, or still orbited by later messages; otherwise the Persona 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. Persona 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: Personas 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 Persona'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. - 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 Persona, peer Personas, 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 Persona output shows a character-specific reaction. This should let resentment, rivalry, consultation offense, and territorial tension emerge without manual state edits.
- The swarm is currently online again through
state/agent-swarm-paused.jsonwith"paused": false. It was restarted deliberately on 2026-05-24 after global doctrine inheritance landed. Keep it under observation: check topic saturation, source-channel routing, inherited global doctrine in Persona prompts, and whether turf/status/consultation events produce character-specific projected pressure and durable state through ordinary turns. - Direct Persona
UPDATE REQUESTandBIFROST TOPICpackets are ignored whileREPO_PERSONA_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 Persona turn/reviewer prompts, provider wrapper prompts, situational social read prompts, source-grounding reminders, consensus-feed prompts, and repo Persona 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 Persona CTB scheduler paths now load through the canonical Persona 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 Persona turn loop into scheduler, context assembler, prompt renderer, candidate parser, policy gate, and transport adapter.
- Repo Persona child prompts now load the full global Codex
AGENTS.mdas inherited doctrine near the top of each Persona turn. This deliberately carries the Prime Directive, rebuild contract, humor doctrine, infrastructure habits, Epiphany stewardship, and Great Colossus mission frame into Persona cognition rather than relying on a local summary shard. - Repo Persona child turns now preserve Discord media context.
SourceMessagecarries attachment metadata, heartbeat Discord fetch caches image attachments under.voidbot/media/discord-images, Persona transcripts show[media: image ... local=...], and owner Codex receives cached images throughcodex exec --image. Non-image attachments are visible as metadata only; Personas are explicitly told not to pretend they saw images unless a local image path was attached. - After those gates exist, run dry/mock Persona 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.