230 lines
10 KiB
Markdown
230 lines
10 KiB
Markdown
# VoidBot Implementation Plan
|
|
|
|
This is the current forward plan for the next larger organs. It is not a changelog and it is not allowed to become a duplicate brain for the canonical map.
|
|
|
|
## Current Aim
|
|
|
|
Stop feature work on moderation, mood, and self-state until the state boundary is rebuilt. The live product goal still stands: Void should answer Discord, remember useful social/project context, retrieve GameCult history/source/lore, and run an unattended moderation/participation loop. The current implementation reaches that goal through JSON projection edits, legacy mirrors, and a swollen memory organ. That is architectural debt wearing a little paper crown.
|
|
|
|
The next priority is to remove JSON from the mutation loop entirely. Agents and scheduled workers should mutate typed CultCache-backed state through explicit tools/APIs, not by editing a whole-state JSON working copy.
|
|
|
|
## Smallest Coherent Architecture
|
|
|
|
### Durable State Model
|
|
|
|
- Postgres owns operational bot state:
|
|
- jobs
|
|
- audit events
|
|
- interaction memory
|
|
- provider runs
|
|
- public usage/rate limits
|
|
- Qdrant owns semantic vectors:
|
|
- Discord history vectors
|
|
- source/lore vectors
|
|
- `.voidbot/` owns operational files:
|
|
- raw Discord archive
|
|
- source archive manifest and repo shards
|
|
- provider artifacts/traces
|
|
- logs/status/backups
|
|
- One polymorphic CultCache-backed state authority owns private Void self-state through typed document kinds:
|
|
- `void.self_profile`: identity, stable values, voice/personality configuration
|
|
- `void.moderation_cursor`: reviewed Discord cursor, open room obligations
|
|
- `void.speech_receipts`: recent delivered replies and dedupe keys
|
|
- `void.thought_memory`: distilled memories, incubation threads, resonance summaries
|
|
- `void.scheduled_runtime`: sleep cycle, speaking pressure, last run summaries
|
|
- `void.candidate_interventions`: drafts or requests that may become speech
|
|
|
|
CultCache does not need a separate physical store per state type; it is already polymorphic. The intended split is ownership by typed document kind and mutation contract, not a herd of little stores in fake mustaches. Each durable fact still has one owner. No top-level mirrors. No editable shadow copy pretending to be a harmless view.
|
|
|
|
### Runtime State Model
|
|
|
|
- Discord request runtime is an immutable request context:
|
|
- prompt/message
|
|
- actor/guild/channel
|
|
- recent room transcript
|
|
- interaction profile
|
|
- optional situational read
|
|
- retrieval hints/results
|
|
- self-state summary projection
|
|
- Scheduled moderation runtime is a bounded pass context:
|
|
- current cursor window
|
|
- outstanding obligations
|
|
- selected memory/thought summaries
|
|
- selected repo/archive evidence
|
|
- proposed state mutations
|
|
- optional speech request
|
|
- Mood drift runtime is deterministic maintenance:
|
|
- read stable self profile
|
|
- update scheduled runtime pressure/sleep fields
|
|
- request memory maintenance through typed operations
|
|
|
|
Runtime context dies at the end of the pass unless an explicit typed mutation preserves a durable fact.
|
|
|
|
### Authoritative Boundaries
|
|
|
|
- Bot/worker/provider/RAG boundaries survive.
|
|
- CultCache self-state has one mutation authority: a typed state service over one polymorphic cache.
|
|
- Agents do not edit state files. They call tools such as:
|
|
- `record_reviewed_messages`
|
|
- `upsert_open_case`
|
|
- `close_open_case`
|
|
- `record_delivery_receipt`
|
|
- `append_distilled_memory`
|
|
- `merge_incubation_support`
|
|
- `queue_candidate_intervention`
|
|
- `retire_candidate_intervention`
|
|
- `update_sleep_cycle`
|
|
- `update_speaking_pressure`
|
|
- Model/agent output crosses the boundary as proposed operations, not as rewritten state.
|
|
- The state service validates, normalizes, dedupes, and writes.
|
|
|
|
### Boundary Messages
|
|
|
|
The clean crossing surface is small JSON command payloads, not a whole JSON state document:
|
|
|
|
```json
|
|
{
|
|
"operation": "upsert_open_case",
|
|
"sourceMessageId": "discord-message-id",
|
|
"status": "pending",
|
|
"summary": "What Void owes the room",
|
|
"lastTouchedAt": "2026-05-16T00:00:00.000Z"
|
|
}
|
|
```
|
|
|
|
```json
|
|
{
|
|
"operation": "append_distilled_memory",
|
|
"kind": "project_seam",
|
|
"subject": "AquariumSynthCSharp",
|
|
"summary": "Workflow cannot own the body.",
|
|
"evidenceRefs": ["source:..."],
|
|
"observedAt": "2026-05-16T00:00:00.000Z"
|
|
}
|
|
```
|
|
|
|
The runner can hand these to a CLI/MCP tool. The store decides what survives.
|
|
|
|
### Derived, Not Stored
|
|
|
|
- Self-state prompt summaries are derived from typed documents.
|
|
- Recent room context is derived from Discord archive and the current poll.
|
|
- Topic saturation is derived from incubation/memory support, not separately stored as another truth.
|
|
- Source coverage is derived from recent evidence refs and repo/archive metadata.
|
|
- Need-to-speak can keep a small pressure field, but its inputs remain receipts, candidates, and time since last speech.
|
|
- Legacy top-level mirrors are deleted. If a consumer needs a projection, render it on demand.
|
|
|
|
### Delete Because It Compensates For Bad Ownership
|
|
|
|
- `.voidbot/private/moderation-agent-state.json` as an editable working projection.
|
|
- `legacyJsonPath`/working-path duality for canonical self-state.
|
|
- Top-level mirror fields beside `moderation_runtime`.
|
|
- Any helper that mutates the JSON projection and commits it back.
|
|
- Prompt doctrine that tells the child agent how to manually preserve state shape.
|
|
- Regex/template police that try to repair semantic sludge after the store allowed sludge to become durable.
|
|
|
|
### Impossible By Construction
|
|
|
|
- An unattended agent cannot corrupt the entire self-state by rewriting one JSON file.
|
|
- Two fields cannot both claim to be the cursor.
|
|
- Repo activity cannot advance by bypassing the state service.
|
|
- Delivery receipts cannot exist only in a status file while state forgets the bot spoke.
|
|
- A projection cannot become authoritative.
|
|
- Unknown fields cannot silently become doctrine just because `.passthrough()` let them in.
|
|
|
|
## Current VoidBot Compared To The Ideal
|
|
|
|
- The bot/worker/provider/RAG/Postgres/Qdrant shape is basically sound.
|
|
- Source archive sharding is sound.
|
|
- Interaction memory being Postgres-backed is sound.
|
|
- The private moderation/self-state foundation is not sound:
|
|
- canonical MessagePack exists, but JSON is still a mutation surface
|
|
- schema validation is too permissive to protect invariants
|
|
- helpers mutate projection files and then commit whole-state changes
|
|
- legacy mirrors keep reintroducing stale truth surfaces
|
|
- `scripts/void-memory-organ.mjs` owns storage cleanup, semantic interpretation, vectors, incubation, sleep consolidation, identity crystallization, agency, and speech candidates in one file
|
|
- the scheduled moderation runner owns lifecycle through a huge prompt instead of a typed runner contract
|
|
|
|
Verdict: stop feature work and rebuild this foundation. The rest of the machine can keep running, but moderation/mood/self-state work should cut toward typed state tools before adding new behavior.
|
|
|
|
## Migration Plan
|
|
|
|
### Commit 1: Name The Boundary
|
|
|
|
- Add typed self-state domain interfaces in core.
|
|
- Define document types and operation payload types.
|
|
- Keep current storage untouched.
|
|
- Verification: typecheck only.
|
|
|
|
### Commit 2: Add Read-Only Typed Projections
|
|
|
|
- Build a self-state service that reads current canonical state and renders the same summaries used by direct replies.
|
|
- Keep JSON projection for the old runner.
|
|
- Verification: compare rendered summary against current loader output on a fixture.
|
|
|
|
### Commit 3: Add Typed Mutation CLI
|
|
|
|
- Extend `scripts/moderation-state-store.mjs` or replace it with a typed command surface for cursor, open cases, receipts, candidates, sleep, speaking pressure, and distilled memories.
|
|
- The CLI writes CultCache directly.
|
|
- Verification: fixture tests for each operation.
|
|
|
|
### Commit 4: Move Repo Activity Cursor Behind The Store
|
|
|
|
- Change `scripts/export-recent-repo-activity.mjs` so it never writes `.voidbot/private/moderation-agent-state.json`.
|
|
- It should emit observed activity and call/store a typed `update_repo_activity_cursor` operation.
|
|
- Verification: run with a temp fixture store; cursor advances without JSON projection writes.
|
|
|
|
### Commit 5: Replace Agent Whole-State Editing With Operation Output
|
|
|
|
- Scheduled moderation child may propose operations and speech, not edit state.
|
|
- Runner applies operations through the typed store.
|
|
- Keep old JSON runner behind a fallback flag for one commit only.
|
|
- Verification: dry run with `-NoPost` applies only allowed operations.
|
|
|
|
### Commit 6: Delete JSON Projection Authority
|
|
|
|
- Remove routine materialization/commit of `.voidbot/private/moderation-agent-state.json`.
|
|
- Keep an explicit debug export command if humans need to inspect state.
|
|
- Remove `legacyJsonPath` from normal paths.
|
|
- Verification: mood drift and moderation dry runs pass without touching JSON.
|
|
|
|
### Commit 7: Cut Legacy Mirrors
|
|
|
|
- Remove top-level mirror reconciliation.
|
|
- Migrate any consumer to typed projections.
|
|
- Delete mirror fields from canonical state.
|
|
- Verification: projection summaries still contain required information; no code references mirror keys.
|
|
|
|
### Commit 8: Split The Memory Organ
|
|
|
|
- Split deterministic cleanup from semantic interpretation.
|
|
- Keep vector/resonance/incubation maintenance behind typed state operations.
|
|
- Move speech-candidate queuing into a candidate intervention service.
|
|
- Verification: existing fixture state produces bounded, typed operations and no whole-state rewrite.
|
|
|
|
### Commit 9: Rebuild Scheduled Runner As A Phase Machine
|
|
|
|
- Replace prompt-owned lifecycle with explicit phases:
|
|
- poll chronology
|
|
- read typed state summary
|
|
- collect evidence
|
|
- ask model for decisions/operations
|
|
- validate/apply operations
|
|
- optionally send speech
|
|
- record receipts/cursor
|
|
- Verification: durable logs show each phase and mutation count.
|
|
|
|
### Commit 10: Resume Product Hardening
|
|
|
|
- Only after JSON mutation and legacy mirrors are gone:
|
|
- public-lane moderation policy
|
|
- admin/forensics surfaces
|
|
- restore drills
|
|
- provider/sandbox scaffolding cleanup
|
|
|
|
## Rules For Editing This Plan
|
|
|
|
- update this note when the larger sequence of work changes
|
|
- do not dump volatile minute-by-minute status here
|
|
- if a lesson changes future belief, record it in `state/evidence.jsonl`
|
|
- if the current next action changes, update `state/map.yaml` and `notes/fresh-workspace-handoff.md`
|