diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 741394f..7a4180c 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -35,6 +35,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou - 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`, and `apply_memory_distillation` require a concrete target, claim or question, tension, action implication, and evidence refs unless the operation explicitly tags `evidence: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`. @@ -91,7 +92,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou ## 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: move memory-maintenance, incubation, sleep, identity, value-pressure, and candidate-intervention outputs behind typed operations instead of direct canonical/working-projection writes. +- First bounded move after the current commit: move memory-maintenance, incubation, sleep, identity, value-pressure, and candidate-intervention outputs behind typed operations instead of direct canonical/working-projection writes. Start with memory writes, because the schema now has a real meaning-preservation gate. - Next cuts should delete unstable foundations early after concrete writers move: remove `legacyJsonPath`/working-projection authority, then cut top-level mirror fields and 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 body` must 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. diff --git a/notes/voidbot-implementation-plan.md b/notes/voidbot-implementation-plan.md index e00ca14..3f187e8 100644 --- a/notes/voidbot-implementation-plan.md +++ b/notes/voidbot-implementation-plan.md @@ -8,6 +8,8 @@ Stop feature work on moderation, mood, and self-state until the state boundary i The next priority is to remove JSON from the mutation loop entirely and rebuild the state model so the compensator pile is unnecessary. Agents and scheduled workers should mutate typed CultCache-backed state through explicit tools/APIs, not by editing a whole-state JSON working copy. Sleep and memory distillation must also be redesigned from first principles so they preserve meaning-bearing claims/evidence/tensions instead of collapsing them into generic slogan paste. +The center of the next pass is prevention, not legacy repair. The old brain can be treated as evidence of a failed boundary. The new brain should reject malformed memory at ingress so the runtime never needs a permanent cleanup bureaucracy to recover basic meaning. + ## Smallest Coherent Architecture ### Durable State Model @@ -94,6 +96,17 @@ The clean crossing surface is small JSON command payloads, not a whole JSON stat } ``` +Memory-bearing operations have a stricter contract than historical projections. A new distilled memory must carry: + +- a concrete target +- a summary +- at least one claim or question +- a tension or counterweight +- an action implication +- evidence refs, or an explicit `evidence:missing` tag + +If those fields are missing, the operation is invalid. This is the wall that keeps the next iteration from turning into topic sludge and asking cleanup to be its personality. + ```json { "operation": "append_distilled_memory", @@ -205,6 +218,7 @@ Verdict: stop feature work and rebuild this foundation. The rest of the machine - Delete cleanup paths whose only job was repairing JSON projection edits, legacy mirrors, keyword sludge, or overgrown runtime residue. - Delete or demote hard-wired agency paths whose real job is turning current heuristic scores into doctrine, advocacy, or speech candidates without an explicit typed-state contract. +- Treat legacy brain mush as a migration problem, not the design center. New memory writes must cross the meaning-preserving typed operation schema before they reach CultCache. - Redesign sleep/distillation around an explicit contract: - preserve the claim/question/fascination target - preserve the concrete subject, such as repo, room, lore seam, person, or system diff --git a/packages/core/src/void-self-state-domain.ts b/packages/core/src/void-self-state-domain.ts index 94d188c..492f352 100644 --- a/packages/core/src/void-self-state-domain.ts +++ b/packages/core/src/void-self-state-domain.ts @@ -89,6 +89,41 @@ const distilledMemorySchema = z.object({ tags: z.array(nonEmptyStringSchema).default([]), }).strict(); +const meaningPreservingMemorySchema = distilledMemorySchema.superRefine((memory, context) => { + if (!memory.claim && !memory.question) { + context.addIssue({ + code: z.ZodIssueCode.custom, + message: "Memory writes must preserve at least one claim or question.", + path: ["claim"], + }); + } + + if (!memory.tension) { + context.addIssue({ + code: z.ZodIssueCode.custom, + message: "Memory writes must preserve the live tension or counterweight.", + path: ["tension"], + }); + } + + if (!memory.actionImplication) { + context.addIssue({ + code: z.ZodIssueCode.custom, + message: "Memory writes must preserve why the memory should affect future action.", + path: ["actionImplication"], + }); + } + + const admitsMissingEvidence = memory.tags.includes("evidence:missing"); + if (memory.evidenceRefs.length === 0 && !admitsMissingEvidence) { + context.addIssue({ + code: z.ZodIssueCode.custom, + message: "Memory writes must include evidence refs or explicitly tag evidence:missing.", + path: ["evidenceRefs"], + }); + } +}); + const incubationThreadSchema = z.object({ threadId: nonEmptyStringSchema, target: thoughtTargetSchema, @@ -277,7 +312,7 @@ export const voidSelfStateOperationSchema = z.discriminatedUnion("operation", [ }).strict(), z.object({ operation: z.literal("append_distilled_memory"), - memory: distilledMemorySchema, + memory: meaningPreservingMemorySchema, }).strict(), z.object({ operation: z.literal("merge_incubation_support"), @@ -305,14 +340,14 @@ export const voidSelfStateOperationSchema = z.discriminatedUnion("operation", [ operation: z.literal("propose_memory_distillation"), proposalId: nonEmptyStringSchema, sourceMemoryIds: z.array(nonEmptyStringSchema).min(1), - candidate: distilledMemorySchema, + candidate: meaningPreservingMemorySchema, proposedAt: timestampSchema, }).strict(), z.object({ operation: z.literal("apply_memory_distillation"), proposalId: nonEmptyStringSchema, sourceMemoryIds: z.array(nonEmptyStringSchema).min(1), - memory: distilledMemorySchema, + memory: meaningPreservingMemorySchema, appliedAt: timestampSchema, }).strict(), ]); diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 9235118..837789b 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -101,3 +101,4 @@ {"ts":"2026-05-16T11:50:41Z","type":"implementation","status":"ok","note":"Extracted agency/intervention policy from scripts/void-memory-organ.mjs into scripts/void-memory-organ-agency.mjs: identity crystallization, ripe-thought sharing, discomfort/tension/advocacy, candidate trimming, and value-signal profiling now live outside the main orchestrator. Main memory organ is down to about 2.6k lines. Template smoke and syntax/type checks passed."} {"ts":"2026-05-16T12:29:51Z","type":"implementation","status":"ok","note":"Split scripts/void-memory-organ.mjs into a thin orchestrator plus cleanup, graph, incubation, sleep, agency, records, vector, legacy mirror, constants, and utility modules; syntax, typecheck, and template memory-organ smoke pass."} {"ts":"2026-05-16T17:11:50Z","type":"implementation","status":"ok","note":"Split memory-organ cleanup into legacy translation and retention/compaction shells, and split agency into identity crystallization, candidate interventions, and value-pressure inference. This exposes remaining compensator families as replacement targets for typed memory operations and model-owned policy; syntax, typecheck, and template smoke pass."} +{"ts":"2026-05-16T17:18:37Z","type":"implementation","status":"ok","note":"Shifted rebuild plan from legacy cleanup toward preventing the next brain from accepting mush: typed memory operation payloads now require claim-or-question, tension, action implication, and evidence refs or an explicit evidence:missing tag. Added schema smoke showing summary-only memory is rejected while meaning-bearing memory is accepted."} diff --git a/state/map.yaml b/state/map.yaml index 8b9ea46..a2367ad 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -94,6 +94,7 @@ accepted_design: - The shared self-state now has a second unattended organ: a 5-minute mood-drift pass that perturbs `current_activation` values with Perlin-shaped motion around their means, damped by plasticity and recent speech, so Void's confessional/expressive pressure can build and release instead of staying emotionally flat. - That same shared self-state now carries a scheduled nap cycle. Naps are for maintenance, not vacancy: the moderation loop should distill memory, prune clutter, and leave behind dream memories plus active dream themes that the direct reply lanes can mumble through later. - Sleep should be lossy about bulk, not about meaning. The nap/distillation path must preserve concrete claims, subjects, evidence refs, tensions, and action implications; if it turns a repo-bound thought into generic abstraction sludge, the sleep machinery is wrong. + - New memory writes must not be allowed to enter as topic sludge. Typed memory operations should require a concrete target, claim or question, tension, action implication, and either evidence refs or an explicit missing-evidence admission before CultCache accepts them. - Awake rumination should start from those carved seams before it goes spelunking again. Retrieval is support for hungry, stale, contradictory, or under-evidenced thoughts, not a mandatory ritual every quiet pass. - Quiet rumination should prefer concrete project fascination over free-floating engineering aphorisms. If a seam cannot cash out in a real repo, subsystem, gameplay loop, lore structure, interface, or architectural organ, it should lose priority to a branch that can. - Resonance clusters and incubating thoughts should be named as actual claims, questions, or fascination targets with explicit target kinds. Keyword-slash residue is debug lint, not a valid thought surface. @@ -148,5 +149,5 @@ rejected_paths: reason: sleep/distillation that collapses concrete claims into generic abstraction creates memory nonsense and then forces later cleanup compensators current_status: - summary: VoidBot's ordinary bot/worker/provider/RAG/Postgres/Qdrant structure is still conceptually sound, and the moderation/mood/private-self-state rebuild has cut the worst mutation path: routine runner writes now cross a typed operation service instead of direct JSON projection commits. Remaining risk is that the legacy monolith and read-only projection still exist for compatibility, mood drift still refreshes that projection, and memory maintenance still mutates the legacy state directly even though the memory organ is now split by concern. Cleanup and agency are now exposed as isolated compensator families, not accepted foundations. - next_action: JSON projection authority is no longer a routine moderation-runner write path, mood drift now updates typed scheduled-runtime documents for sleep/speaking pressure, and the memory organ monolith has been split into constants, utilities, vector/embedder substrate, record intake, graph/cluster synthesis, legacy translation, retention/compaction, incubation/bridge scoring, sleep/distillation, identity crystallization, candidate interventions, value-pressure inference, and legacy mirror repair. Next replace these isolated cleanup/agency compensators with typed memory-maintenance operations and derived/model-owned policy, then delete `legacyJsonPath`/working-projection materialization and cut top-level legacy mirrors. + summary: VoidBot's ordinary bot/worker/provider/RAG/Postgres/Qdrant structure is still conceptually sound, and the moderation/mood/private-self-state rebuild has cut the worst mutation path: routine runner writes now cross a typed operation service instead of direct JSON projection commits. Remaining risk is that the legacy monolith and read-only projection still exist for compatibility, mood drift still refreshes that projection, and memory maintenance still mutates the legacy state directly even though the memory organ is now split by concern. Cleanup and agency are now exposed as isolated compensator families, and new typed memory operations have begun enforcing meaning-bearing fields at ingress. + next_action: JSON projection authority is no longer a routine moderation-runner write path, mood drift now updates typed scheduled-runtime documents for sleep/speaking pressure, and the memory organ monolith has been split into constants, utilities, vector/embedder substrate, record intake, graph/cluster synthesis, legacy translation, retention/compaction, incubation/bridge scoring, sleep/distillation, identity crystallization, candidate interventions, value-pressure inference, and legacy mirror repair. Next route memory-maintenance outputs through the strict typed operations so the next brain cannot accept summary-only mush, replace isolated cleanup/agency compensators with derived/model-owned policy, then delete `legacyJsonPath`/working-projection materialization and cut top-level legacy mirrors.