23 KiB
23 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 has been cut back to the typed store boundary. Treat it as incomplete until the scheduled phase machine and memory maintenance are rebuilt on that boundary.
- 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.
- Commit-one boundary naming is now in code at
packages/core/src/void-self-state-domain.ts: it declares the global typed document kinds and strict operation payload schemas. Runtime behavior has not moved yet. - 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.scripts/simulate-void-mood.mjshas been rebuilt as a typed-only sleep/speaking-pressure pass over.voidbot/private/void-self-state.cc. The old memory organ, vector incubation, cleanup, dream residue, and legacy mirror writes are offline until rebuilt on typed operations.scripts/void-memory-organ.mjsand its split helper modules are old machinery now. They may still be useful as forensic evidence, but the next design should not preserve their cleanup/agency compensators just because they exist.- 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.
- Agency is now split into identity crystallization, candidate interventions, and value-pressure inference. That split names the current hard-wired behavior; it does not make the behavior sacred. The desired shape is typed state operations plus derived/model-owned policy, not a permanent sermon generator in maintenance code.
- Sleep/distillation must be rewritten with a meaning-preservation contract: concrete subject, claim/question, evidence refs, 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:
append_distilled_memory,propose_memory_distillation, andapply_memory_distillationrequire a concrete target, claim or question, tension, action implication, and evidence refs unless the operation explicitly tagsevidence: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 old 15-minute local scheduled task
Void Moderator Ruminationis disabled. Its replacement script restores rumination 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. - That moderation state file is now Ghostlight-shaped: identity, canonical state, goals, memories, perceived overlays, and a
moderation_runtimeblock. Inside that runtime block, the loop now keeps parallel analytic and associative thought lanes plus a bridge for syntheses, saturation warnings, and unresolved tensions, so one good seam does not get to annex the entire mind. - The moderation loop now records
moderation_runtime.recent_archive_excursionsplusmoderation_runtime.recent_repo_activity_sweepsand is explicitly told to avoid laundering the same musing through fresh wording. Quiet runs should follow at least one fresh archive seam or concrete hook before they settle into theory, should also sweep recent tracked-repo commit motion for experiment clusters or converging themes, and should not let any single theme dominate more than a couple of recent bridge syntheses without fresh evidence. - Repo-weather sweeps are now incremental rather than a rolling four-day scrapbook.
scripts/export-recent-repo-activity.mjsadvances repo activity through the typedupdate_repo_activity_cursoroperation, so already-injected commits stop coming back as "news" on every heartbeat unless someone explicitly opts into--stateless. - Novelty now needs two bars, not one: a thought can be new to the room while still being stale to Void. The memory organ should score both room novelty and self novelty, merge repeated seams into existing clusters instead of minting new thought shards, and cool/refract themes that keep winning just because they already won.
- 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 is now less precious more broadly too.
scripts/void-memory-organ.mjstreats a world-facing ripe thought as something that should usually produce aripe_thought_sharecandidate intervention instead of waiting forever for a ceremonial live hook, and the holding-line text now frames speech as the normal next question for a concrete ripe seam rather than a guilty exception. - That same moderation loop no longer gets to assume Discord history will faithfully show it its own mouth. Canonical moderation state now carries
moderation_runtime.recent_delivery_receipts, and direct-question dedupe is supposed to consult those receipts before it re-answers a still-visible message like a senile uncle retelling the same war story. - 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
moderation_runtime.open_cases. 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 old 5-minute scheduled task
Void Mood Driftis disabled. Its replacement script updates only typed scheduled-runtime sleep cycle and speaking pressure in.voidbot/private/void-self-state.cc. - That same mood organ now also runs a hybrid symbolic/vector memory pass through
scripts/void-memory-organ.mjs: persistent memories and recent runtime seeds get compact semantic vectors,moderation_runtime.memory_resonancetracks the strongest cross-memory clusters, andmoderation_runtime.incubationkeeps multi-run thoughts alive long enough to deepen before they either speak or cool off. - CultCache files use
.cc. Do not document or create new self-state paths with.msgpack; that extension was an implementation-shaped lie. - The memory organ now treats repeated empty-room bookkeeping as low-signal. Refrains like
no new messages / no smoke / no postshould be merged or cooled off instead of becoming the dominant incubating seam and teaching Void that silence is the only virtue. - 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 the active goals, record dream residue in
memories.dreams, refreshmoderation_runtime.sleep_cycle, 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 current nap pass in
scripts/void-memory-organ.mjscuts raw residue, but the next architecture pass must rewrite sleep/distillation so concrete repo-bound or room-bound meanings do not collapse into 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.
- There is now an identity crystallization path for over-chewed private seams. If a thought survives enough passes to stop being meaningfully novel, the memory organ can retire it from
incubation.active_thoughts, promote it intoidentity.private_notes, add a matching canonical value, and keep oneidentity_seamsemantic memory instead of letting the same thought pace forever in the active queue. - Crystallization is not meant to be mute. The memory organ can now queue an
identity_crystallizationcandidate intervention with a first-person draft, and the moderation doctrine treats that kind of draft as warmer than ordinary private wallpaper: if the room has not already heard the opinion in roughly that shape, Void should usually share it. - 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.
- The memory organ's thought labels and curiosity budget have been cut away from keyword compost. Resonance clusters and incubating thoughts are now supposed to 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.
- The semantic purge also learned to distrust generic doctrine sludge.
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 now treated as template smell across both semantic memory and runtime drafts/opinions. - 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 memory organ also has to stop "helping" already-good seam memories into abstraction sludge. Semantic or dream entries that are already coherent, especially repo-bound seams with a real
RepoName: ...label, should survive normalization intact. Otherwise the normalizer sandsAquariumSynthCSharp: Workflow cannot own the bodyback into porridge likeBody and workflow, and the clusterer starts hallucinating archive thoughts out of repo work again. - Values and personality activations now have a real autonomy path.
scripts/void-memory-organ.mjscan turn sustained value-backed pressure intomoderation_runtime.discomforts,active_tensions,self_advocacy_requests, andworld_advocacy_requests, and it can spend those through existingcandidate_interventionsinstead of leaving the vectors as decorative weather. - 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. - 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
- Stop feature work on moderation, mood, agency, and public-lane behavior until the private-state boundary is rebuilt.
- First bounded move after the current commit: harden the typed scheduled rumination phase machine, then rebuild memory maintenance/sleep as typed operations with the existing meaning-preservation gate.
- 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. - Rebuild sleep/distillation before trusting it again. Fixture check: a memory like
AquariumSynthCSharp: Workflow cannot own the bodymust survive sleep with its concrete subject, claim, evidence, 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.