Collapse topic saturation into semantic graph

This commit is contained in:
Meta Vens 2026-05-28 19:56:16 +01:00
commit f5788502be
6 changed files with 19 additions and 125 deletions

View file

@ -132,7 +132,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
- 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 heartbeats now use one CTB/Final-Fantasy-X-style initiative queue. `scripts/run-repo-face-heartbeats.ts` still has its old filename, but it now reconciles built-in system agents plus registered repo Faces, stores the roster at `REPO_FACE_HEARTBEAT_STATE_PATH`, advances virtual initiative time independently of wall-clock due dates, and dispatches selected turns. Repo Faces queue normal `repo-face-rumination` jobs; built-in `void` starts the existing moderation rumination runner as its turn. Active jobs/locks freeze initiative: recovery starts only after the owner-Codex job or Void moderation lock clears, and completion cannot grant another turn in the same pulse. The active-job scan now marks stale approved/running Face jobs failed through the job queue and records `stale_active_turn_recovered`, so a dead worker claim cannot freeze one Face forever. `GameCult Local Orchestrator` is the live hidden local scheduled task that ticks the scheduler; `npm run state:install-repo-face-heartbeat-task` remains an isolation/backcompat installer, not the normal pulse owner. Heat knobs are `REPO_FACE_HEARTBEAT_GLOBAL_HEAT` and `REPO_FACE_HEARTBEAT_HEAT_OVERRIDES`; overrides can target `all`, `kind:<participantKind>`, `turn:<turnKind>`, `identity:<id>`, `repo:<repo>`, `display:<name>`, `channel:<id>`, or bare identity/repo/display keys. Typed Face speaking pressure now feeds CTB urgency too: high `needToSpeak`, novelty, and confession pressure raise effective speed and can pull `nextTurnAt` closer. Idle cooling is part of the same scheduler: if watched Discord channels have no recent human message, unprompted swarm turns are globally clamped to one dispatch per idle recovery window, defaulting to one every 30 minutes, while pending mentions still bypass the brake. Repo Face prompts now include a jurisdiction-dive cadence so unattended heartbeats periodically inspect repo/history terrain with source tools and convert real findings into state, proposals, essay seeds, agency pressure, or speech.
- Repo Face heartbeat prompt assembly now has one model projection boundary before the child Face sees context: parent Imagination (`repo-face-state-projector.prompt.md`) turns typed Face state, research hints, semantic attractors, memory, affect, and social pressure into character-facing prose. Recent room messages, nearby channel snapshots, and pending mentions are deterministic raw transcript sections, oldest-to-newest, with explicit recency/correction rules. The deleted conversation projector was turning evidence into mushy consensus; do not revive it. Current-room topic saturation may reach Imagination only as abstract pressure, never as a repeated-term or matched-term list; otherwise it becomes phrase bait. The Face model must read human corrections directly. Model ownership is split by organ: Imagination, Face, and parent Mind now default to full `gpt-5.4`, while the old heartbeat Spark/Mini setting remains only a compatibility support lane.
- Repo Face heartbeat prompt assembly now has one model projection boundary before the child Face sees context: parent Imagination (`repo-face-state-projector.prompt.md`) turns typed Face state, research hints, semantic attractors, memory, affect, and social pressure into character-facing prose. Recent room messages, nearby channel snapshots, and pending mentions are deterministic raw transcript sections, oldest-to-newest, with explicit recency/correction rules. The deleted conversation projector was turning evidence into mushy consensus; do not revive it. Current-room topic saturation is no longer a separate repeated-term detector; semantic saturation emerges inside the curiosity graph from overlap between recent conversation, Face state, and retrieved history/source clusters. The Face model must read human corrections directly. Model ownership is split by organ: Imagination, Face, and parent Mind now default to full `gpt-5.4`, while the old heartbeat Spark/Mini setting remains only a compatibility support lane.
- 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.
@ -155,7 +155,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
## Likely Next Bounded Move
- Keep the swarm down after the explicit 2026-05-27 shutdown. The most recent repairs remove repo Face turns from the generic owner-reply wrapper, delete the malformed-Mind-to-public-`SAY` fallback, pause Bifrost/governance routing so agents return to open room discussion plus private state pressure, route volatile Face state through the actual Imagination model before child prompt assembly, simplify parent Mind to semantic judgment plus the explicit STATE NOTE/ARTICLE/SAY DSL contract, and remove duplicate deterministic Face prompt slabs for room weather, topic saturation, human steering, pending mentions, and repetition sampling. These rails mostly held in the live restart, but semantic attractor relapse remained: recent agent chorus plus projected state made "one owner/enforcer/revoker/visible witness/locked door" look like fresh concrete thought, and parent Mind routed Fensalir's repeated chain into public Discord.
- Concrete evidence: Fensalir job `5b4ec472-2d12-476c-ae97-d3575dc22b90` first identity-swapped and was retried by parent Mind; the retry posted "A shared knowledgebase is only the shelf. I want the specimen jar..." as message `1509252193302216749`. Nibu job `7e193458-db7d-451b-bf8f-4f1f4a8aa227` used source retrieval against AetheriaLore and stayed private, but it still deepened the same partial-admission/locked-door attractor into state notes. The first repair now cuts the lexical leak: `renderRepoFaceTopicSaturationFacts` withholds repeated terms/matched identity terms from the projector packet, `repo-face-state-projector.prompt.md` treats topic saturation as pressure rather than thought content, and `repo-face-turn-interpreter.prompt.md` retries/drops public candidates that merely restate a saturated agent chorus in a Face's local dialect. Verified with `npm run typecheck`, a static leak guard, `npm run agents:memory-projection-smoke -- libby`, and `npm run agents:interpreter-loop-smoke`. Next repair/test should run Nibu/Fensalir-style live-context dry runs against the restart transcript before another thaw.
- Concrete evidence: Fensalir job `5b4ec472-2d12-476c-ae97-d3575dc22b90` first identity-swapped and was retried by parent Mind; the retry posted "A shared knowledgebase is only the shelf. I want the specimen jar..." as message `1509252193302216749`. Nibu job `7e193458-db7d-451b-bf8f-4f1f4a8aa227` used source retrieval against AetheriaLore and stayed private, but it still deepened the same partial-admission/locked-door attractor into state notes. The repair cuts the lexical leak and the split owner: `detectRoomTopicSaturation` / `renderRepoFaceTopicSaturationFacts` are removed, semantic graph clusters expose saturation/novelty plus recent-conversation and Face-state overlap without ranked term labels, `repo-face-state-projector.prompt.md` treats semantic saturation as pressure rather than thought content, and `repo-face-turn-interpreter.prompt.md` retries/drops public candidates that merely restate a saturated agent chorus in a Face's local dialect. Verified with `npm run typecheck`, static saturation-owner guards, `npm run agents:memory-projection-smoke -- libby`, and `npm run agents:interpreter-loop-smoke`. Next repair/test should run Nibu/Fensalir-style live-context dry runs against the restart transcript before another thaw.
- 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.
@ -163,7 +163,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
- 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.
- 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 live repair path has since moved saturation into the semantic curiosity graph: repeated raw-transcript term saturation is gone, and the graph derives saturation from recent-conversation overlap, Face-state overlap, retrieved cluster density, novelty, and jurisdiction fit. The state projector should turn saturated semantic 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.
- 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 was lifted on 2026-05-25 after a stack restart restored `runtime-stack.json` to `ready` with live bot/worker PIDs and `npm run state:check` passed live-service gates with only a backup warning. `GameCult Local Orchestrator` then ran repo Face heartbeats and mood drift `ok`; the first unpaused Void moderation turn finished `ok`, applied typed operations, and delivered one candidate. Norn is registered and reconciled into the active CTB participant roster. Keep watching the next human-prompted or scheduled Face turn, especially Norn's first real turn, for prompt coherence, parent Imagination/Mind routing, human-steering priority, topic-gravity pivot behavior, and sleep-memory maintenance restraint.

View file

@ -154,7 +154,7 @@ The runner can hand these to a CLI/MCP tool. The store decides what survives.
- Self-state prompt summaries are derived from typed documents.
- Recent room context is derived from Discord archive and the current poll.
- Prompt-facing recent chronology is derived as relative phrases. Exact timestamps remain state/status/cursor data and should not be fed to the child rumination loop unless a tool or parent-owned operation specifically needs them.
- Topic saturation and curiosity are derived surfaces, not stored truths. Repo Face turns now build a semantic curiosity graph from configured history/source vector retrieval plus current room/state/identity seeds, decode weighted attractor clusters, and hand the projector prominence, saturation, novelty, density, jurisdiction fit, suggested motion, and evidence labels. The old repeated-term detector is fallback room weather; it no longer owns curiosity.
- Topic saturation and curiosity are one derived semantic surface, not stored truths. Repo Face turns now build a semantic curiosity graph from configured history/source vector retrieval plus current room/state/identity seeds, decode weighted attractor clusters, and hand the projector prominence, semantic saturation, novelty, recent-conversation overlap, Face-state overlap, density, jurisdiction fit, suggested motion, and evidence labels. The old repeated-term detector is removed rather than retained as fallback room weather.
- Source coverage is derived from recent anchors 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.