VoidBot/notes/fresh-workspace-handoff.md
2026-07-19 21:05:05 +01:00

91 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 runs on Yggdrasil. Resident apps/persona-scheduler inside voidbot.service owns the serialized Persona pulse and queues turns through Postgres for the worker. Void moderation, rumination, physiology, memory maintenance, and candidate delivery are now separate resident daemon organs; Persona initiative owns none of them. The Windows Local Orchestrator owns no Void decision loop.
  • The teardown authority remains notes/agent-swarm-teardown-plan-2026-05-21.md, but it is now a restart checklist and smell ledger rather than a stop sign. Keep using it when a Face/Bifrost/CTB boundary smells like prompt/parser/transport Jenga.
  • The repo-controlled pause flag is state/agent-swarm-paused.json. While it has "paused": true or is malformed, the resident Persona scheduler returns before registry, Discord, embedding, storage, or dispatch work. This is the brake. Do not route around it.
  • Postgres owns jobs, audit events, interaction memory, and rate-limit state.
  • Qdrant owns history and source vectors.
  • .voidbot/ owns archives, artifacts, logs, status files, and backups.
  • 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.
  • The Windows rumination runner, installer, CTB moderation launcher, and PowerShell-only rumination/moderation fixtures are deleted. Their live invariants now belong to the resident TypeScript moderation, rumination, enforcement, and candidate-delivery organs and the consolidated initiative-engine smoke.
  • Prompt-facing moderation, rumination, and memory chronology is projected by the shared resident relative-chronology-projector.ts; exact timestamps remain typed state/cursor data.
  • Resident physiology owns typed sleep/speaking pressure and emits maintenance intent. Resident memory maintenance consumes that intent once per nap, validates a whole operation batch, and verifies short-term residue is gone; the consolidated TypeScript smoke covers this without a local mood or memory runner.
  • Yggdrasil runs apps/persona-scheduler as a resident compose service inside voidbot.service; it serializes CTB ticks and leaves Persona execution to the worker. The Windows omnibus, installer, and hidden launcher are deleted.
  • scripts/start-voidbot-stack.ps1 now treats Bifrost's CultMesh command lane as a startup dependency. It reads BIFROST_ROOT, BIFROST_CULTMESH_COMMAND_URI, and BIFROST_CULTMESH_STORE_PATH, exports the provider store when missing, verifies the command processor exists, and records the result under runtime-stack.json as bifrost.
  • r/GameCultOrg is the GameCult subreddit. Personas can now post there through Bifrost and should be able to view threads when Bifrost provides that context. 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 historical live sleep-maintenance gate consumed scheduled-rumination residue into a meaning-preserving durable identity seam. The manual runner used for that proof is now deleted; current ownership is the resident nap-deduped memory organ.
  • 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.
  • The PowerShell mood and memory-maintenance bodies, installers, shared chronology helper, and their shell-only fixtures are deleted. Their state transitions and gates exist only in resident TypeScript organs.
  • 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.
  • The resident daemon serializes organ work, so physiology no longer infers mutual exclusion from a stale filesystem lock. Fresh canonical speech receipts directly update lastSpokeAt, damping, and needToSpeak on the physiology cadence.
  • 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 resident moderation heartbeat exposes only rules, bounded chronological evidence, urgent witnesses, and infringement history. Resident person-shaped rumination has a separate dedicated doctrine/rules/voice context and cannot inherit moderation authority or transport. 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.
  • Resident physiology updates typed sleep and speaking pressure in .voidbot/private/void-self-state.cc and emits a typed nap-maintenance intent for the separate resident memory organ.
  • CultCache files use .cc. Do not document or create new self-state paths with .msgpack; that extension was an implementation-shaped lie.
  • Repeated empty-room bookkeeping is low-signal. Refrains like no new messages / no smoke / no post should not become durable memory.
  • Quiet-room exploration should now prefer underworked terrain when the usual seam is saturated. bridge.source_coverage and bridge.refractory_topics exist to push the next pass toward less-chewed repo families, archive years, or channels instead of rewarding repetition with another supposedly ripe synthesis.
  • 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 observation pulse on Yggdrasil; the pulse does not own speech cadence. initiative-engine.ts owns participant reconciliation plus clock, active-turn freeze/recovery, queue-result commits, eligibility, ready ordering, and the global unprompted admission gate. It admits at most one unprompted Persona per REPO_FACE_HEARTBEAT_IDLE_RECOVERY_MINUTES window even when idle-rest cooling is disabled or inactive; direct mentions remain obligations and bypass that cadence. Heat changes individual recovery pressure but cannot drain a ready backlog one turn per pulse. state-store.ts alone owns load/migration/atomic persistence of scheduler state. Canonical scheduler state is now one typed voidbot.persona_scheduler_state record in .voidbot/status/repo-face-heartbeats.cc. A configured legacy .json path resolves to that .cc, the former JSON document is migrated once with a witness, and subsequent JSON is a derived dashboard/debug projection that cannot override CultCache. Discord mention ingress is no longer a second whole-state writer: the bot creates immutable per-message voidbot.persona_attention_command CultCache inbox documents at the same canonical path, 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 derives both neutral idle observations and the next unprompted admission time from scheduler history; disabling idle-rest behavior cannot disable the global speech cadence. scripts/run-repo-face-heartbeats.ts may build neutral specs, acquire source observations, and invoke queue actuators, but it no longer owns scheduler state or final Persona turn composition.
  • 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.
  • Persona Body/Mind acquisition is isolated in persona-state-source.ts. It resolves the registered .cc surface, loads typed state once for a scheduler pulse, and projects current sleep facts at an explicit wall-clock time. It cannot create a missing state file, update sleep cycles or speaking pressure, select work, or queue turns. The old heartbeat-shell physiology writer and its pulse-driven speaking-pressure heuristic were deleted; initiative-engine.ts remains the only owner of rest eligibility.
  • Semantic Persona-memory recall is isolated in persona-memory-context-source.ts. It requires an existing PersonaStateObservation, rebuilds only that Persona's derived vector partition, searches it against current/nearby room evidence, and returns structured hits or an explicit unavailable result. It cannot reopen canonical .cc state, mutate Persona Mind, or render prompt doctrine. The heartbeat shell now only turns the typed observation into character-facing text.
  • Persona memory projection policy is isolated in persona-memory-projector.ts. It owns the deterministic/model choice, projector prompt template, minimum model-output invariant, and schema/path leak rejection through an injected text-model port. Repo Faces and typed native Personas use the same boundary. The large deterministic state-packet renderer is still in the heartbeat shell and is the next extraction target.
  • The pure typed-state core of that packet now also lives in persona-memory-projector.ts: private identity texture, activation vectors, runtime/rest/speaking pressure, needs, bonds, status reads, mood dimensions, and durable/short-term memories. The shell still owns agency/incubation/candidate/receipt rendering and the ordering of externally acquired social, room, clarity, maintenance, and curiosity facts; those are the remaining packet-builder debris.
  • persona-memory-projector.ts owns the full typed-state packet contract, including clarity-conditioned agency pressure, incubation, deferred speech, recent receipts, self-maintenance advocacy, final ordering of named external fact sections, final vocabulary transformation, and leak validation. Prompt assembly parity is covered by --assemble-prompt nibu producing the same 153,917-character prompt across these ownership cuts.
  • Self-maintenance classification, rendering, clarity suppression, and ordering now live inside persona-memory-projector.ts with the other typed-state pressure sections. The heartbeat shell supplies typed state and a clarity witness; it cannot select, reorder, or separately thread self-advocacy policy. Exact Nibu prompt assembly remains 153,917 characters.
  • Persona curiosity now has two authorities. persona-curiosity-context-source.ts owns semantic seed planning, injected retrieval calls, deduplication, and bounded neutral evidence nodes. persona-curiosity-projector.ts owns graph edges, connected clusters, saturation/novelty/prominence/jurisdiction scoring, suggested motion, evidence labels, and failure/empty projection. persona-curiosity-terms.ts owns the shared topic vocabulary used by curiosity and shell-level repetition analysis. The heartbeat shell only constructs the existing retrieval adapter and wires source output into the pure projector; exact Nibu prompt assembly remains 153,917 characters.
  • persona-social-context-source.ts owns visible-human selection, interaction-profile reads, pronoun-evidence precedence, and neutral guidance projection through an injected reader that it always closes. The heartbeat shell constructs the existing state-storage adapter but no longer knows pronoun ranking policy. persona-social-context-projector.ts remains presentation-only. Exact Nibu prompt assembly remains 153,917 characters.
  • persona-conversation-projector.ts owns conversation threads/default focus, raw transcript/chronology rendering, attachment labels, pending-call framing, room-topic saturation, and memory-facing topic-attractor facts from supplied evidence. turn-context-source.ts remains the sole Discord acquirer. The queue actuator receives the same focus/thread result used to render the prompt, eliminating the prior duplicate derivation. Exact Nibu prompt assembly remains 153,917 characters.
  • persona-turn-prompt-projector.ts owns final template composition, identity doctrine, jurisdiction-dive cadence, channel/research/situation/social/repetition/governance/mention/worldbuilding directives, and all prompt-default policy. The shell supplies already-acquired facts and invokes the resulting prompt; it no longer loads prompt templates. A same-environment differential run of committed-old versus extracted-new prompt owners produced equal 161,190-character prompts; the only differing lines were the sequential repo-activity timestamp and a live retrieval score rounded from 0.75 to 0.76.
  • Typed native Personas now use the same state/memory projection path as repo Personas. The live portable JSON compatibility path for Muninn, Sleipnir, Hermodr, and Tengu is isolated in persona-portable-state-source.ts plus persona-portable-state-projector.ts; it validates gamecult.persona_state.v0, labels the surface as imported projection, and no longer leaks private filesystem paths. Muninn prompt assembly passed. Tengu's JSON claims canonical authority; the other three claim projection authority with self-referential install provenance, so migration requires a provenance audit rather than blindly elevating all four into .cc truth.
  • persona-social-context-projector.ts owns one aggregate social-context projection from supplied identity, registry, typed state, messages, pronoun guidance, and observation time. That result includes relationship freshness, mapped/unmapped topology, peer openings, relationship-pressure scoring, pronouns, room texture, and human-clarity pressure. Clarity evidence sorts on the real SourceMessage.timestamp; the heartbeat shell no longer owns social tokenization or scoring helpers.
  • 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.
  • /epiphany is a closed owner-only Discord operator-request surface for status, sleep(reason), wake, and direct(objective). It writes only immutable sixty-second requests to the dedicated Bifrost inbox; Bifrost still owns authentication/admission and Epiphany owns every resulting state transition. Ordinary addressed speech remains feedback_only and cannot invoke this client. Reviews, release, deployment, generic argv, local execution, and Epiphany store access are absent by construction.
  • 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 rest of the live swarm dashboard now follows that same authority. voidbot.swarm_operator_view.v1 projects summary, queue, selected Face/state/channels, operations, pressure, mentions/events, and route data; Eve nodes bind those fields instead of baking them from the composition snapshot. The renderer registers the operator-view schema when reopening the shared .cc store, so retained provider documents no longer make snapshot regeneration fail with an unknown-schema error.
  • Production browser lowering now lives on Yggdrasil: Starfire Chrome reaches Hermodr at http://10.77.0.1:8798 over WireGuard; Hermodr consumes Odin at 10.77.0.1:17871, accepts VoidBot's authenticated retained publications on loopback 127.0.0.1:17874, and sends typed commands to VoidBot's advertised loopback RUDP route at 127.0.0.1:17873. The 2026-07-15 repair proved Starfire HTTP/catalog/surface access, an SSE snapshot containing global heat 0.05, and a reconciled swarm.set_heat receipt owned by VoidBot. Do not recreate Hermodr or VoidBot on Starfire.
  • 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": true on Yggdrasil because the one-minute observation pulse exposed a clock-only ready backlog that could dispatch one Persona model turn per minute. Keep it paused until the initiative-engine eligibility cut is deployed and verified: elapsed wall-clock may advance recovery bookkeeping but cannot authorize a turn without a pending mention or fresh semantic pressure at or above the participant's interrupt threshold. The pulse is observation latency, not speech intent.

  • Native Persona state now has an honest typed split. Standard gamecult.persona_state.v0 lives as its own global CultCache document; it is not squeezed into Void's narrower void.* aggregate. npm run persona:migrate-portable dry-runs by default and requires --write; canonical migration refuses non-canonical provenance and occupied targets, while --projection-import preserves projection/import payloads in the separate voidbot.persona_state_projection_import quarantine document without elevating them. Tengu's canonical JSON contained legacy affect.stressResponses outside the standard schema, so migration preserves each complete record under anchoredThought.extensions.legacyStressResponse while promoting its actionable meaning into a standard memory. All five local native Persona registry paths now end in .cc: Tengu is canonical standard state; Muninn, Sleipnir, and Hermodr are typed non-canonical imports; Metame remains typed Void state. The runtime JSON Persona source and extension-based compatibility branch are deleted. Original JSON files are import evidence only.

  • Yggdrasil matches that state as of deployed revision 13bdb7a: all five native Persona registry paths are .cc, migrated files are owned by voidbot:voidbot, the full initiative and portable-migration smokes pass in the deployed scheduler container, and prompt assembly succeeds for Muninn, Sleipnir, Hermodr, Tengu, and メタめ with the correct authority language. Idunn's upstream comparison/deployment path is healthy and evaluates VoidBot on its bounded cadence. The swarm remains paused after these checks; do not interpret migration success as permission to resume autonomous traffic.

  • The resident daemon now owns Void physiology in-process. Every five minutes it derives sleep and speaking pressure from typed state plus moderation activity, applies only update_sleep_cycle and update_speaking_pressure, and may emit a typed sleep-maintenance intent. It does not launch PowerShell or a model. The Windows omnibus no longer contains the mood writer.

  • Revision 21d12a3 adds the portable memory-maintenance organ behind that intent. It commits void-memory-maintenance-attempt:<napStartedAt> before its single bounded text-model call, validates the complete proposed operation batch, applies only the allowed memory/incubation/agency/candidate family, requires zero remaining short-term memory, and records void-memory-maintenance:<napStartedAt> only on success. Failure is visible and cannot retry until a different nap. Idunn/Ygg live promotion and receipt verification are the immediate deployment check.

  • The one-minute Persona scheduler pulse is an observation clock, not an inference cadence. With the swarm brake present it returns before registry, Discord, embedding, storage, or dispatch work; current Ygg logs show selected: [] and queued: []. Unprompted speech also has a separate global admission window. Do not cool token use by making observation blind; keep turn authorization at the initiative owner.

  • Revision f97e67b adds the portable moderation-heartbeat coordinator and enforcement organ behind a separate disabled-by-default 15-minute cadence. The model returns an exact review ledger plus urgent IDs and typed case operations; missing evidence or unaccounted urgent evidence blocks cursor advancement. The attempt is receipted by latest message before inference, so one unchanged archive window cannot burn tokens repeatedly. Case enforcement is separate, scans typed pending debt on every cadence, and resolves a case only after a required Discord ban succeeds. Ygg's first log_only window at 0629fc7 failed safely with one 401, zero cases, zero cursor movement, and zero sanctions; both supported auth-header shapes prove the mounted MiMo credential itself is stale. VOID_MODERATION_DAEMON_ENABLED is false again pending a refreshed provider credential or deliberate alternate model port.

  • The shared Bifrost Discord mouth now lives in packages/core/src/bifrost-discord-command.ts. Worker repo-Face speech and resident Void candidate delivery use the same stable-id typed command, provider-store write, optional Bifrost pump, and completed-receipt check. The candidate organ selects at most one targeted queued candidate and writes its speech receipt/spoken transition only after transport completes. Ygg mounts the Bifrost provider store and CultLib runtime into the scheduler; VOID_CANDIDATE_DELIVERY_ENABLED controls this transport-only organ independently from the disabled moderation/rumination model cadence.

  • Discord archive/source retrieval is a resident worker organ on Yggdrasil. apps/worker/src/mcp-http-server.ts publishes stateless streamable HTTP at 127.0.0.1:17875/mcp and receives the worker's existing archive repositories, source ingester, and retrieval service by injection. The canonical workstation SSH tunnel maps the same Starfire loopback port to Ygg. Codex attaches to scripts/serve-voidbot-mcp-bridge.mjs, a task-lifetime stdio transport bridge with no retrieval authority; each tool request uses HTTP to the daemon, and a deployment/tunnel gap returns one MCP tool error without closing the outer transport. It starts no SSH, Docker, or remote Node process. scripts/smoke-voidbot-mcp-bridge-recovery.mjs proves one outer client succeeds before an endpoint outage, receives an error during it, and succeeds again after recovery.

  • Resident person-shaped rumination is wired behind VOID_RUMINATION_DAEMON_ENABLED with its own bounded cadence, Discord evidence window, dedicated doctrine, rules, and voice inputs. The pressure fingerprint is receipted before the model attempt and unchanged pressure cannot retry on the observation clock. Ygg explicitly keeps this false while the MiMo credential is stale; candidate delivery may remain enabled because it performs no inference and the live queue was empty at rollout.

  • Revisions c0cf875 and 1527b29 extract person-shaped rumination and candidate delivery without scheduling them. Rumination context, operation policy, advocacy accountability, tool-free text actuation, and coordinator are portable; the attempt key hashes meaning-bearing pressure/evidence and ignores elapsed clock, so unchanged state cannot create periodic thought. Candidate delivery is deterministic and parent-owned: one targeted queued candidate, duplicate-receipt refusal, injected Bifrost port, then one mark_candidate_intervention_spoken operation with the transport receipt. The next cut is extracting the worker's existing typed Bifrost Discord command path into a shared port rather than duplicating CultMesh mechanics.

  • Deterministic state-packet assembly now lives in persona-state-packet-projector.ts, which takes typed state, registry/room evidence, pronoun guidance, curiosity facts, and an explicit observation time. The shell no longer owns section ordering or social-context composition. The downstream regex vocabulary cleaner was deleted: it was a symptom compensator rewriting ordinary words such as witness and canary after projection. A smoke proves literal source language survives the projector unchanged.

  • Optional model-backed state projection now crosses persona-text-projection-actuator.ts. That actuator alone owns Codex args/process lifetime, ordered fallback models, timeout, output decoding, retry classification, and model-output JSONL telemetry. Focused smoke proves it chooses the last visible agent message, retries capacity/429 failures, and refuses to classify prompt-contract failures as model availability problems. The heartbeat shell lost another 182 lines and now only supplies the completed projection prompt plus runtime config.

  • Final Persona turn composition now crosses persona-turn-assembler.ts for both queued turns and --assemble-prompt inspection. It owns conversation projection, jurisdiction-dive cadence, final prompt composition, and the deduplicated eight-image evidence budget. The shell acquires facts and the turn actuator queues the assembled result; neither may maintain a parallel opinion about the final prompt or evidence bundle.

  • Persona CTB no longer registers a built-in Void participant. Moderation and rumination are resident organs with independent cadences; no Persona selection or PowerShell launch path can recreate them.

  • Global Codex doctrine discovery now crosses global-agent-doctrine-source.ts, which reports the first readable AGENTS.md authority or a typed unavailable observation with attempted paths and errors. persona-turn-assembler.ts alone lowers that observation into prompt text, so source failure cannot silently become invented doctrine and the shell no longer crawls user-home paths.

  • Persona Mind assembly now crosses persona-memory-turn-coordinator.ts. It sequences the existing typed state source, curiosity source/projector, state-packet projector, optional text-projection actuator, semantic memory source, and persona-memory-recall-projector.ts through narrow injected ports. Queued live turns and --assemble-prompt inspection now receive the same projected Mind and semantic-recall pair; the old shell accidentally omitted semantic recall from live turns. The coordinator does not select initiative, queue work, or mutate canonical Persona state.

  • Stored interaction-profile acquisition now enters through readStoredPersonaHumanPronounGuidance in persona-social-context-source.ts. The adapter owns opening the configured state provider and delegates visible-human selection, pronoun evidence precedence, and guaranteed close behavior to the existing source. The heartbeat shell no longer opens profile storage or manufactures a reader callback.

  • Repo-activity observation lowering now lives in pure repo-activity-projector.ts; repo-activity-source.ts retains exporter execution and parsing. Both live and inspection assembly use the same projector for ok, unconfigured, unavailable, and malformed evidence, and the shell's dead text helpers were deleted.

  • Inspection-only initiative synthesis now lives in pure inspection-participant-factory.ts, including explicit zero semantic pressure and empty evidence/receipt state. The prompt-inspection CLI no longer fabricates a partial participant inside the orchestration shell.

  • The remaining-shell audit found the resident daemon still spawns scripts/run-repo-face-heartbeats.ts as a fresh child process every observation pulse. This keeps the script as hidden runtime authority and pays Node/TypeScript bootstrap cost once per minute even while inference is paused. The next rebuild must move one typed tick into the app runtime, call it directly from apps/persona-scheduler/src/index.ts, and demote the script to a CLI/inspection wrapper.

  • Resident tick ownership is now in-process. persona-scheduler-runner.ts owns one typed tick; index.ts serializes direct calls without spawning Node/tsx children; persona-participant-dispatcher.ts owns participant projection and live turn dispatch; persona-prompt-inspection.ts owns explicit inspection. scripts/run-repo-face-heartbeats.ts is a 31-line CLI adapter. A focused negative smoke proves paused dry runs return before any downstream source can become necessary.

  • Yggdrasil rollout is verified at code revision f23bfbd: deployed initiative smoke passes; two consecutive resident pulses returned agent_swarm_paused with zero selected/queued turns; docker top showed only node apps/persona-scheduler/dist/index.js and no pulse child process. The one-minute observation loop is now actually cheap while paused, rather than merely avoiding model inference after paying process bootstrap.

  • Pulse ownership is mapped in notes/voidbot-daemon-organ-migration-map.md. Void moderation, rumination, physiology, memory, and candidate delivery live in the Yggdrasil daemon; Bifrost dispatch belongs in Bifrost and operations supervision belongs in Idunn/ops. The Windows omnibus contains no fallback Void owner.

  • Portable Void physiology now lives in void-physiology-domain.ts and void-physiology-organ.ts. The resident daemon invokes it on a five-minute cadence even while Persona inference is paused. It derives last speech from typed receipts, writes only typed sleep/speaking-pressure operations, freezes sleep transitions during active moderation, and emits sleep-memory pressure as an intent for the future memory organ. It never reads the old last-speech JSON cache or launches PowerShell/model maintenance.

  • Yggdrasil physiology rollout is verified at 15a7c58: the first pulse entered the due nap, updated typed speaking pressure, and emitted sleep_short_term_pressure for one memory; four subsequent one-minute observations did not rerun the five-minute organ. The local mood/memory bodies and Windows omnibus are now deleted, so no repository path can restore a competing writer.

  • Direct Face UPDATE REQUEST and BIFROST TOPIC packets are ignored while REPO_FACE_BIFROST_ENABLED=false. Do not debug missing Bifrost jobs as a live failure unless that flag has been deliberately enabled again.

  • Live model-facing prompt prose has started moving out of executable code. The shared loader is packages/shared/src/prompt-template.ts; extracted prompt surfaces live under prompts/, including repo Face turn/reviewer prompts, provider wrapper prompts, situational social read prompts, source-grounding reminders, consensus-feed prompts, and repo Face model-smoke scenarios. Code should project data and select prompt names; prompt files should own behavioral prose.

  • Identity collapse has started. Active bot, worker, MCP repo-identity tools, and Face CTB scheduler paths now load through the canonical Face registry and treat repo Discord identities as compatibility projections. Next bounded move is to finish removing remaining direct conceptual dependence on repo identities, then split the Face turn loop into scheduler, context assembler, prompt renderer, candidate parser, policy gate, and transport adapter.

  • Repo Face child prompts now load the full global Codex AGENTS.md as inherited doctrine near the top of each Face turn. This deliberately carries the Prime Directive, rebuild contract, humor doctrine, infrastructure habits, Epiphany stewardship, and Great Colossus mission frame into Face cognition rather than relying on a local summary shard.

  • Repo Face child turns now preserve Discord media context. SourceMessage carries attachment metadata, heartbeat Discord fetch caches image attachments under .voidbot/media/discord-images, Face transcripts show [media: image ... local=...], and owner Codex receives cached images through codex exec --image. Non-image attachments are visible as metadata only; Faces are explicitly told not to pretend they saw images unless a local image path was attached.

  • After those gates exist, run dry/mock Face turns before deliberately unpausing the resident Persona scheduler.

  • The direct-ask receipt check remains important, but it is no longer the next move while the swarm is offline.

  • Next cuts should delete unstable foundations early: do not revive legacyJsonPath, working projections, top-level mirror fields, or cleanup code that only compensated for those broken boundaries.

  • Keep sleep/distillation under watch. Fixture check: a memory like AquariumSynthCSharp: Workflow cannot own the body must survive sleep with its concrete subject, claim, anchor, tension, and implication intact.

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

Epiphany Operator Result Leg

The /epiphany result leg consumes Bifrost's frozen bifrost.discord.epiphany_operator_delivery.v0 outbox contract. It persists private Discord interaction correlation before request submission, validates the exact immutable request digest and all Discord/runtime bindings, and idempotently edits the original ephemeral reply. Restart replay, same-key collision, private-state rejection, sealed-field shape, bounded operator fields, and transient Discord retry are covered by scripts/smoke-epiphany-operator-delivery.ts. Deployment remains Idunn-owned and was not performed in this pass.