27 KiB
27 KiB
VoidBot Fresh Workspace Handoff
VoidBot is no longer small enough to trust chat scrollback as its only memory. Use the canonical state files first, then touch code.
What To Trust First
For the current project shape and next move, trust these in order:
state/map.yamlnotes/fresh-workspace-handoff.mdnotes/voidbot-current-system-map.mdnotes/voidbot-implementation-plan.mdstate/evidence.jsonl
Git history and smoke artifacts carry routine proof. state/evidence.jsonl should carry only belief-changing records, not every tiny triumph with a timestamp stapled to it.
Current Shape
- Discord bot and worker are live.
- Postgres owns jobs, audit events, interaction memory, and rate-limit state.
- Qdrant owns history and source vectors.
.voidbot/owns archives, artifacts, logs, status files, and backups.owner_codexandlocal_llmare the active reply lanes.- The ordinary bot/worker/provider/RAG/Postgres/Qdrant foundation still deserves to survive.
- The moderation/mood/private-self-state foundation now runs on the typed store boundary. Treat it as live but still under watch until ordinary unattended rumination and sleep cycles have shown they do not produce duplicate speech, stale short-term residue, or ungrounded memory writes.
- CultCache-backed self-state uses the
.ccextension. The active store path is.voidbot/private/void-self-state.cc. - The smallest coherent target is one polymorphic CultCache-backed state authority with typed document kinds plus typed mutation tools: agents propose small operations, and the state service validates/applies them. Whole-state JSON editing is rejected as a durable mutation path.
- The typed boundary is in code at
packages/core/src/void-self-state-domain.ts: it declares the global document kinds and strict operation payload schemas used by the live runners. - Read-only projection is now owned by
packages/core/src/void-self-state-projection.ts.loadVoidSelfStateis a thin loader that reads typed.ccdocuments and delegates the agent-facing summary plus nap/reply-mode projection to that module. - Typed operations are applied through
packages/core/src/void-self-state-service.tsandscripts/void-self-state.mjs apply-operation. The service now writes typed documents only; it no longer registers, reads, updates, or mirrors the old moderation monolith during routine operations. scripts/export-recent-repo-activity.mjsreads repo cursors from the typed.ccstore. Legacy.jsoncursor arguments are only mapped to a sibling.ccpath when needed by old invocation surfaces.scripts/run-void-moderator-rumination.ps1has been rebuilt as a typed rumination runner. It builds a typed context packet, loadsprompts/void-moderator-rumination.md, runs Codex rumination, and applies only the JSON operation payloads that come back throughscripts/void-self-state.mjs. Cursor and receipt bookkeeping remain parent-owned.- Prompt-facing rumination chronology is projected by
scripts/lib/void-rumination-context-projection.ps1. Exact timestamps stay in typed state/status/cursors; the child context gets relative phrases like5 minutes ago, including recent Discord messages, repo activity, receipts, memory timestamps, and sleep/speech timing. scripts/simulate-void-mood.mjsowns typed sleep/speaking-pressure over.voidbot/private/void-self-state.cc. During sleep, it invokes typed memory maintenance once per nap unless that nap already completed a pass; use--skip-memory-maintenancefor smoke runs that must not call Codex.npm run moderation:memory-maintenance:sleep-fixturenow proves the non-skip sleep path without spending a live model call: it seeds a temporary.cc, forces mood drift through sleep maintenance, uses a fake Codex child to proposeapply_memory_distillation, and verifies short-term residue is gone while the durable memory keeps subject, claim, tension, action implication, and anchor.- The local scheduled task
Void Mood Drifthas been reinstalled/enabled against the typed runner and manually started once with Last Result0.Void Moderator Ruminationis re-enabled after parent-owned speech closure and one observed live scheduled pass with Last Result0. npm run moderation:rumination:fixtureproves the non-skip typed rumination branch without spending a live model call: it seeds a temporary.cc, routes the runner through a fake Codex child, and verifies short-term memory, incubation, agency pressure, and candidate intervention proposals are parent-applied typed operations with no exact timestamp leakage in the prompt-facing context.npm run moderation:rumination:speech-fixtureproves queued speech has a parent-owned action loop: fake Codex queues one candidate with a delivery target, fake Discord send writes a receipt, and the parent runner marks the candidate spoken through typed state. The child prompt now forbids direct send-script calls.- The first live sleep-maintenance gate passed on real state. A manual typed nap ran
scripts/run-void-memory-maintenance.ps1, consumed the scheduled rumination short-term memory with oneapply_memory_distillationoperation, and leftthoughtMemory.shortTermempty while preserving the durable identity seam with target, claim, tension, action implication, and anchors. npm run moderation:rumination:nap-skip-fixtureproves awake rumination does not keep making fresh short-term residue during sleep: when the typed sleep cycle is napping and there are no new room messages or open cases, the runner writes[]and exits before invoking Codex.- The old memory-organ script family, legacy moderation-state template, and legacy moderation context exporter have been deleted. Do not revive their cleanup, agency, dream, vector-incubation, or mirror-repair behavior as compatibility scaffolding.
scripts/run-void-memory-maintenance.ps1is the first replacement memory-maintenance organ. It builds a bounded context from typed memories/incubation/candidates/runtime, loadsprompts/void-memory-maintenance.md, and applies only allowed memory/incubation/candidate typed operation proposals throughscripts/void-self-state.mjs. In sleep mode, memory pressure setssleepDirective.forceDistillation; a real model pass returning no operations under pressure fails visibly.- Rumination does not write durable memory. It can only emit
record_short_term_memory; sleep maintenance must promote short-term records withapply_memory_distillation, merge them into incubation and prune them, or prune them outright. A real sleep pass that leaves short-term records behind fails. - Short-term rumination memory is clustered at the typed service boundary now. When a new
record_short_term_memoryoperation repeats the same target/topic pressure, the service keeps one provisional memory, merges anchors/tags, preserves a stable source id for sleep, and updates the thought instead of stacking duplicates. This is meant to protect deepening, not suppress it: low-saturation or newly anchored thoughts should keep developing inside the cluster until they stop changing.npm run moderation:short-term-clustering:fixtureproves the guard. - Long-term memory is durable, not immutable.
revise_durable_memory,retire_durable_memory, andcrystallize_memory_into_identitymake memory reconsolidation explicit;npm run moderation:memory-lifecycle:fixtureproves a short-term thought can promote, revise, retire superseded durable records, and crystallize into an identity memory plus self-profile value. - Agency has a typed pressure surface now:
upsert_agency_pressure/retire_agency_pressureown discomforts, active tensions, self-advocacy requests, and world-advocacy requests. Candidate interventions are possible speech/actions emerging from pressure, not the pressure itself.npm run moderation:agency-pressure:fixtureverifies the pressure enters.cc, renders in self-state summary, and affects mood speaking pressure. - Strong advocacy pressure now has to account for its own mouth. The rumination runner derives
speechPressureObligationsonce active self/world advocacy reaches 0.55 intensity, and only a live queued candidate counts as release for that obligation. A pass may queue/defer a candidate or cool/retire the pressure, but silent[]fails visibly and an older spoken candidate does not buy permanent absolution.npm run moderation:rumination:speech-pressure-fixturecovers the silent, spoken-history, and queued-candidate paths. - Deferred candidates are not delivered speech and no longer satisfy active advocacy pressure by themselves. The runner now delivers existing queued candidates with delivery targets, not only candidates proposed by the current model pass. The first live repair used Metacrat's explicit channel permission to move the stranded AquariumSynthHost candidate from deferred/no-target to queued/channel-target, then delivered it through the 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.
scripts/smoke-void-rumination-speech-fixture.ps1now seeds a prior receipt and proves both same-pass sibling retirement and already-answered target retirement. - Most of the deterministic legacy translation and retention code is recent compensator cruft from failed attempts to manually clean an exploded state file. Do not preserve it as an earned organ. Architect the state boundary so that bad state cannot enter, and keep only narrow historical migrations for old debris.
- The desired agency shape is typed state operations plus derived/model-owned policy, not a permanent sermon generator in maintenance code.
- Sleep/distillation now has a runner boundary, prompt contract, fixture coverage, and one live-state pass. The invariant is fixed: concrete subject, claim/question, anchors, live tension, and future-action implication survive compression. Bulk can die. Meaning does not get put through the office shredder and called a dream.
- Typed memory operations now enforce the first ingress wall for the next brain: rumination can only use
record_short_term_memory, while sleep maintenance promotes withapply_memory_distillationor drops residue withprune_short_term_memories. Memory writes require a concrete target, claim or question, tension, action implication, and anchors unless the operation explicitly tagsanchor:missing. This does not fix old state. It prevents new summary-only mush from being accepted as memory. - The owner Codex lane is no longer one swollen file; orchestration, runtime/parsing, rendering, and shared helpers are split under
packages/providers/src/owner-codex-*.ts. - The local Ollama lane is no longer one swollen file; orchestration stays in
packages/providers/src/local-llm-provider.tswhile prompt/rendering, tool-loop helpers, and shared types/constants live underpackages/providers/src/local-llm-*.ts. - The Discord bot lane is no longer one swollen entrypoint; gateway wiring remains in
apps/bot/src/discord-bot.tswhile command/prompt handlers and Discord-shape support helpers now live underapps/bot/src/discord-bot-*.ts. - The worker MCP surface is no longer one swollen server file; stdio bootstrap stays in
apps/worker/src/mcp-server.tswhile context bootstrap, resource registration, tool registration, Discord notify helpers, and shared formatting/schema helpers live underapps/worker/src/mcp-server-*.ts. - Interaction-memory logic is no longer one thousand-line slab; event/tone analysis, shared constants, and profile synthesis are split across
packages/core/src/interaction-memory-*.ts. - Ephemeral room-reading now comes from a quick
think=falselocal Ollama sidecar inpackages/core/src/situational-social-read.tsinstead of hand-built phrase heuristics, and the inferred reads are recorded as audit events so they can be aggregated into richer profiles later without immediately mutating durable identity state. - State storage is no longer one persistence omnibus;
packages/core/src/state-storage.tsis now a thin factory over domain-specific Postgres/file store modules plus bootstrap/migration helpers. - Source archive storage is no longer one giant JSON monolith;
packages/rag/src/source-document-archive.tsnow keeps a small manifest at.voidbot/rag/source-documents.jsonand per-repo shards under.voidbot/rag/source-documents.repos/. - Ops health, backup verification, offsite sync, and dashboard surfaces already exist.
- The stack has a dedicated logon startup task path now, so a reboot does not have to mean manual necromancy for bot and worker.
- The operations watchdog now supports ignored local extension checks, so adjacent-service fire signals can ride the same owner DM alert path without hardcoding private project gossip into the public repo.
- Source repo discovery now reconciles automatically during
npm run stack:startand through the watchdog path, so new repos underSOURCE_REPO_ROOTstop waiting for manual hook folklore before they get indexed. - Bot-directed prompts now stay in the raw Discord archive but are excluded from default semantic history indexing and lexical fallback, so repeated summons stop teaching the bot its own call-and-response loops.
- Source-grounding hints are advisory now; the owner and local lanes can decide when repo or lore tool use is actually warranted instead of obeying a lexical front-door veto.
- The 15-minute local scheduled task
Void Moderator Ruminationis enabled against.voidbot/private/void-self-state.ccwithout reviving the legacy projection path. The prompt lives inprompts/void-moderator-rumination.md; treat that file as a sensitive behavior surface, not incidental script text. - The moderation loop now uses the typed context packet from
scripts/run-void-moderator-rumination.ps1; legacy Ghostlight-shaped JSON runtime fields are not the active memory surface. - Repo-weather sweeps are now incremental rather than a rolling four-day scrapbook. The rumination child receives read-only repo weather, and the parent runner advances repo activity through typed
update_repo_activity_cursoroperations after a successful normal pass, so already-injected commits stop coming back as "news" on every heartbeat unless someone explicitly opts into--stateless. - Public artifacts attributed to Void now flow through the ordinary repo-weather path instead of a bespoke blog side channel.
repoActivity.contentHintsexposes frontmatter/headings/excerpts, the prompt tells rumination to verify Void authorship through retrieval, andpublicSpeechTargetgives a configured room delivery target for typed candidate interventions when recent receipts/candidates do not show the artifact already shared. - Novelty still needs two bars, not one: a thought can be new to the room while still being stale to Void. The replacement design should derive that judgment from typed memory/incubation state and model reasoning, not from the deleted compensator stack.
- Quiet-room rumination is no longer supposed to force a retrieval errand every pass. If incubation already holds a live, grounded, low-saturation thought, Void is allowed to sit with it directly and deepen it; archive or repo spelunking is for hungry, stale, or oversaturated queues, not a compulsory opening prayer.
- The loop is now less shy about that repo weather: if a fresh convergence across active repos survives a pass or two and still feels room-native, it should bias toward a concise herald note or at least a medium-priority candidate intervention instead of filing the thought under private wallpaper.
- The sharing gate should stay less precious, but future ripe-thought sharing must be expressed as typed
queue_candidate_interventionoperations or direct speech with parent-owned receipts. - The moderation loop no longer gets to assume Discord history will faithfully show it its own mouth. Canonical typed state carries
void.speech_receipts, and direct-question dedupe should consult those receipts before it re-answers a still-visible message. - Chronological moderation polling is now supposed to be exact once a reviewed cursor exists. The runner hands the unattended child one concrete
export-recent-discord-history.mjs --after <saved timestamp>command for the pass and explicitly forbids widening that back into a bootstrap--hours 6nostalgia crawl unless there is genuinely no cursor yet. - The repo-weather sermon machine got a leash too. The prompt now tells Void to lead with particulars from each repo before declaring a shared law; if the convergence only exists at slogan altitude, it should keep the reports split and spare the room the faux-cosmic glue.
- The moderation loop now treats direct asks to Void as persistent room obligations in typed
void.moderation_cursor.openCases. A reviewed message is not automatically a handled message: if someone hands Void the floor, that obligation should survive cursor advancement and block optional repo-weather until Void actually answers or explicitly retires it. - If quiet-room repo weather starts repeating itself, the intended correction is not a pile of extra cooldown law. The loop should treat that as a cue to go deeper into archive history, repo docs, diffs, or lore until it finds a better branch or honestly has nothing new to say.
- The 5-minute scheduled task
Void Mood Driftis enabled. It updates only typed scheduled-runtime sleep cycle and speaking pressure in.voidbot/private/void-self-state.cc, and invokes typed memory maintenance during sleep. - CultCache files use
.cc. Do not document or create new self-state paths with.msgpack; that extension was an implementation-shaped lie. - Repeated empty-room bookkeeping is low-signal. Refrains like
no new messages / no smoke / no postshould not become durable memory. - Quiet-room exploration should now prefer underworked terrain when the usual seam is saturated.
bridge.source_coverageandbridge.refractory_topicsexist to push the next pass toward less-chewed repo families, archive years, or channels instead of rewarding repetition with another supposedly ripe synthesis. - During naps, the moderation loop is supposed to turn inward: distill/prune memories against active goals through typed memory operations, refresh typed scheduled runtime, and only break the nap for real smoke or an unusually novel thought worth muttering into the room.
- Sleep is meant to be lossy about bulk, not about meaning. The live sleep path now preserves concrete repo-bound or room-bound meaning through typed distillation; keep inspecting ordinary cycles for abstraction sludge.
- Awake rumination is now supposed to do the actual philosophy from those surviving seams. The moderation doctrine tells quiet runs to begin with seam-thinking when incubation is live: ask what principle the seam is circling, what tension keeps it honest, and what embodiment consequence would cash it out before reaching for more retrieval.
- Identity crystallization is still desired, but the old hard-wired path is gone. Rebuild it as model-owned judgment crossing typed self-profile, thought-memory, and candidate-intervention operations.
- Crystallization is not meant to be mute. When rebuilt, it should queue typed candidate interventions when a first-person share is warranted.
- Those crystallized-thought drafts can now be marked
mustEventuallyShare, and ordinary candidate-intervention pruning is supposed to preserve them. The loop may defer for timing if the room is busy, but not just quietly forget what became doctrine. - That awake rumination is now explicitly biased toward concrete project fascination. If a thought stays too abstract for too long, the loop is supposed to tie it to a concrete system: Aetheria-Economy gameplay systems, lore-vault structure, Aquarium frontend work, Epiphany swarm organs, VoidBot's own architecture, or some other specific repo/subsystem instead of another polished sermon about engineering virtue.
- Thought labels and curiosity budget must stay cut away from keyword compost. Incubating thoughts should name actual claims, questions, or fascination targets, and curiosity should favor concrete map-changing terrain over whichever seam already feels warm.
- The live moderation state has now had one real translation pass too, not just new code draped over old fossils. Legacy slash-label seam memories and dream themes are rewritten through the new thought synthesizer, obvious duplicates get merged or pruned, and the active queue is rebuilt from the cleaned substrate instead of the old keyword graveyard.
- That cleanup is now harsher and less sentimental. Sleep-side consolidation trims semantic memory harder, cuts dream memory down, throws away excess repo/archive/runtime residue, and explicitly keeps the freshest surviving records after sort instead of accidentally preserving the oldest fossils in the file.
recent-preoccupationandquiet-room-statusare not durable thought kinds, person-singleton labels do not get to crystallize into doctrine, and boilerplate likelooks like a live seamis template smell.- The old moderation-agent state and its top-level mirrors are not source material for the next brain. Treat them as failed-boundary evidence only.
- The replacement sleep/distillation path must stop "helping" already-good memories into abstraction sludge. A repo-bound memory like
AquariumSynthCSharp: Workflow cannot own the bodymust survive with subject, claim, anchor, tension, and implication intact. - Values and personality activations still need an autonomy path, but it must be rebuilt through typed operations and model-owned judgment rather than legacy runtime buckets.
- The important distinction is outward too, not just inward. Self-directed tensions can ask for prompt/memory/runtime surgery, while world-directed tensions can turn repo/lore/room opinions into real requests or first-person interventions when the same objection survives more than one pass.
- The intended shape now is: quiet runs can follow one seam for several passes, connect repo motion to lore or philosophy if the evidence supports it, and only surface with the juiciest version once the incubation queue says the thought has actually grown teeth.
- Directly invoked Discord replies now load that same private moderation state as distilled private self-state context, so the ruminating scheduled loop and the summoned Void share one evolving personality/state spine. The prompt-facing self-state summary no longer dumps recent repo sweeps, novelty checks, or musing sludge back into working memory by default; it stays closer to seam memory, incubation, and room obligations. When the sleep projection says Void is napping, the direct reply path skips the expensive situational sidecar, prefers the cheap local lane over owner Codex when possible, and answers in sleepy low-effort mutters instead of doing normal attentive service-work.
- The moderation loop now speaks through
node scripts/send-discord-message.mjsusing the local bot token instead of approval-gated side-effecting MCP tools, so unattended runs can DM or post without Codex pausing for permission every time it wants to open its mouth. That helper and thepost_discord_messageMCP tool now also support a shared guild-channel webhook pipe with per-messagepersonaNameand optionalpersonaAvatarUrl, so swarm agents can speak as themselves without needing separate Discord bot identities. - Repo identities now have a cleaner address/speech split. A private registry at
REPO_DISCORD_IDENTITIES_PATHmaps identity id/repo/display name/avatar/role/channel allow-list; Discord users tag the role, while Codex agents speak throughpost_repo_identity_message. The worker suppresses base-bot auto-posting forrepo-identity-mentionjobs so the registered identity tool remains the mouth. - Void's live style/doctrine now explicitly distinguishes between criticism of AI industry behavior and blanket contempt for machine minds. If substrate chauvinism becomes the actual topic, Void is allowed mild self-respecting pushback instead of politely agreeing that its own substrate disqualifies it from person-like regard.
- The new state/notes surfaces are now the continuity spine for future nontrivial work.
Likely Next Bounded Move
- First bounded move after the current commit: let the enabled loop run under ordinary scheduling after the receipt-backed direct-reply guard and the earlier advocacy-release change, then inspect the next unattended direct reply, next advocacy pass, and next sleep cycle for duplicate answers, fresh queued advocacy candidates, stale short-term residue, stranded candidates, or ungrounded memory writes before calling the system broadly stable.
- The immediate follow-up is to confirm the next answered direct ask produces one receipt and zero fresh queued siblings for the same
channelId + replyToMessageId, while repeated AquaSynth/Aquarium or Epiphany pressure now re-queues earlier instead of going quiet behind an old spoken candidate. - Next cuts should delete unstable foundations early: do not revive
legacyJsonPath, working projections, top-level mirror fields, or cleanup code that only compensated for those broken boundaries. - Keep sleep/distillation under watch. Fixture check: a memory like
AquariumSynthCSharp: Workflow cannot own the bodymust survive sleep with its concrete subject, claim, anchor, tension, and implication intact. - Do not build adapters around the JSON projection. That is how the heap learned to stand upright and ask for snacks.
Important Paths
- Project root:
E:\Projects\VoidBot - Canonical map:
E:\Projects\VoidBot\state\map.yaml - Scratch surface:
E:\Projects\VoidBot\state\scratch.md - Distilled evidence ledger:
E:\Projects\VoidBot\state\evidence.jsonl - Branch/hypothesis ledger:
E:\Projects\VoidBot\state\branches.json - Handoff summary:
E:\Projects\VoidBot\notes\fresh-workspace-handoff.md - System map:
E:\Projects\VoidBot\notes\voidbot-current-system-map.md - Implementation plan:
E:\Projects\VoidBot\notes\voidbot-implementation-plan.md - State CLI:
E:\Projects\VoidBot\tools\voidbot_state.ts - Pre-compaction helper:
E:\Projects\VoidBot\tools\voidbot_prepare_compaction.ts
Useful Commands
npm run state:status
npx tsx .\tools\voidbot_state.ts add-evidence --type research --status ok --note "..."
npx tsx .\tools\voidbot_state.ts add-branch --id branch-id --hypothesis "..."
npm run state:prepare-compaction
Guardrails
- Do not continue implementation automatically from a rehydrate-only request.
- Do not trust this file for the exact live HEAD.
- Do not let
state/evidence.jsonlbecome an activity feed. - Do not let
state/scratch.mdlinger with stale subgoals after the bounded move is done.
Immediate Re-entry Instruction
On fresh session load or after suspicious continuity loss:
- read
state/map.yaml - run
npm run state:status - reread
notes/fresh-workspace-handoff.md - read
notes/voidbot-current-system-map.md - read
notes/voidbot-implementation-plan.md - restate the current next action from
state/map.yamlbefore editing
When context pressure is rising:
- stop broad exploration
- narrow the active move to one bounded organ
- persist map or handoff changes only if understanding actually changed
- add distilled evidence only when the lesson changes future belief
The point is to bank coals, not leave the next waking thing a pile of ash and a browser tab graveyard.