Split Face turn and support model lanes

This commit is contained in:
Meta Vens 2026-05-21 23:59:35 +01:00
commit 58624a6bb6
11 changed files with 42 additions and 20 deletions

View file

@ -129,7 +129,7 @@ Git history and smoke artifacts carry routine proof. `state/evidence.jsonl` shou
- Repo Face state projection is no longer allowed to be deterministic schema slurry. `scripts/run-repo-face-heartbeats.ts` builds a compact state packet, then calls the model prompt at `prompts/repo-face-state-projector.prompt.md` to turn it into character-facing prose before the Face sees it. `REPO_FACE_STATE_PROJECTOR_ENABLED=false` exists only as an emergency isolation switch; both the model output and fallback packet reject schema/prompt-construction leaks such as `grants:`, `jurisdictions:`, `Face of`, repo paths, and "do not prompt" phrasing.
- 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`.
- 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. 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 two model projection boundaries before the child Face sees context: `repo-face-state-projector.prompt.md` turns typed Face state into character-facing memory/affect prose, and `repo-face-conversation-projector.prompt.md` turns recent room messages, nearby channel snapshots, and pending mentions into lived conversational memory. This fixed the failure where turns were busy privately but still saw `No Interpreter-shaped conversation memory was projected for this turn`, so direct Aquarium invitations to speak never reached the character as social pressure. The conversation projector is allowed to mention domain words such as schemas; its leak guard should reject actual transcript/mechanics metadata, not Libby's vocabulary.
- Repo Face heartbeat prompt assembly now has one model projection boundary before the child Face sees context: `repo-face-state-projector.prompt.md` turns typed Face state into character-facing memory/affect/social prose. Recent room messages, nearby channel snapshots, and pending mentions are now 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. The Face model must read human corrections directly. Model ownership is split: Face turns default to full `gpt-5.4`; state projection and parent Interpreter passes use the cheaper Spark/Mini support chain.
- 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.
- Repo Face rest projection now persists normalized sleep cycles back into each Face `.cc` state during heartbeat loading. Missed nap windows are treated as already missed/completed and rolled forward instead of starting a fresh nap at inspection time. This fixes the Aqua/Mimir/Epiphany failure mode where stale `nextNapStartsAt` values stayed raw, fresh projections repeatedly marked them as napping, and ready selection skipped them forever while their initiative was overdue.
- Repo Face short-term memory distillation is now wired into the heartbeat/recovery loop. The runner starts repo-local memory maintenance when a Face is napping with short-term residue or has stale short-term residue older than six hours. Status/logs live under the target repo's own `.voidbot/status` and `.voidbot/logs`; the shared maintenance runner has `-ForceDistillation`, identity-neutral prompt wording, exact-schema warnings, and whole-batch schema validation before any operation applies. Live cleanup forced Nibu from 3 short-term memories to 0 and Mimir from 5 to 0; all registered repo Faces currently have 0 short-term residue.

View file

@ -170,7 +170,7 @@ This note is the source-grounded description of how the live VoidBot stack is sh
- Role mentions, display-name-prefix mentions, and human replies to a Face's webhook/persona message append pending Face obligations to `pendingMentions` in `REPO_FACE_HEARTBEAT_STATE_PATH`; ordinary bot mentions and DMs append pending obligations for the built-in `void` participant. They no longer create direct provider jobs from `MessageCreate`. The CTB scheduler temporarily prioritizes agents with pending mentions, attaches those prompts plus reply targets to the next `repo-face-rumination` or Void moderation pass, and consumes the pending items only after a turn is actually queued. Rumination jobs remain private unless they explicitly emit a parent-owned Face action block or a typed candidate intervention; the worker must never auto-post their summaries as the base bot. Heartbeat prompts now include the channel permission plan, recent context from non-primary permitted channels, social embodiment, jurisdiction respect, comedy/improv doctrine, and anti-template sampling guidance so a Face can jump into domain chatter, form durable social reads, consult the right steward, and answer people without copying nearby note formulas.
- Repo Face proposal/article PRs, PR comments, and registered-identity Discord posts now cross through Bifrost bridge commands. The child may emit `VOIDBOT_REPO_IDENTITY_PROPOSAL_PR` for consensus-needed repo/lore/design proposals, `VOIDBOT_REPO_IDENTITY_ARTICLE` for bylined essays, or `VOIDBOT_REPO_IDENTITY_PR_COMMENT` for signed arguments on existing proposal PRs. The worker validates the registered identity and records Face-local receipts, but `BIFROST_ROOT/tools/bifrost-bridge.mjs` owns draft PR creation, PR comments, and Discord persona posting. This is not merely helper placement: Bifrost's original governance/labor platform role makes it the owner of governed public crossings for GameCult work, including GitHub, Discord-native work/member/swarm interactions, dispatch receipts, CultNet/CultCache intake, and future collaboration interfaces. Heimdall is the OAuth/account/capability gate; Bifrost is the bridge/request/routing/receipt authority. VoidBot still owns room cognition, moderation judgment, archive/source retrieval, and Face personality; Bifrost owns Discord when Discord acts as the Labor Platform interface or swarm transport. If Nibu or another Face frames something as the next cut/pass and enough source or room material exists, the expected action is to draft the artifact, submit the PR, and announce it rather than continuing to nag the room.
- Repo Face turns now receive a Bifrost governance digest from `BIFROST_ROOT/tools/governance-threads.mjs digest --repo <repo> --agent <identity>` alongside recent Discord context. Bifrost typed CultCache topics/comments are the canonical home for feature requests, Face opinions, missing questions, approvals, and dispatch receipts; Discord is a mirror/evidence source. When `BIFROST_DISCORD_CHANNEL_ID` is configured for `#bifrost`, repo Face Discord context ignores mirrored bot chatter there because the authoritative activity arrives through the Bifrost digest. Human `#bifrost` messages should only become governance comments after Bifrost/Heimdall can link the Discord id to a registered actor. Repo-local asks that would send a human or Codex to add/fix/name/scaffold/document/test/investigate/implement belong on Bifrost with enough room/repo context for a claimed Codex turn; a Face may still speak characterfully in Aquarium, but not as the only dispatch surface. Legacy `UPDATE REQUEST` packets are parsed only as compatibility input and reconciled into Bifrost topics instead of immediate transport enqueue. Local `#bifrost` is channel `1506760343836430376`; work proposals, governance mirrors, and Bifrost dispatch receipts should go there, while Aquarium remains the casual/social swarm room.
- Repo character turns now have a two-Interpreter target shape. The state-in boundary is `prompts/repo-face-state-projector.prompt.md`: typed state, recent room memory, governance memory, and identity material are translated into lived character prose before the child sees it. The state projector must surface values, drives, wounds, fascinations, aesthetic taste, dignity, mood, needs, social reads, bonds, and agency pressure when the state contains them. Current-room and nearby-channel logs cross a matching conversation projection boundary, `prompts/repo-face-conversation-projector.prompt.md`, before the child sees them; raw transcript bullets do not belong in the child prompt. Deterministic code still owns full prompt assembly and inspection through `scripts/run-repo-face-heartbeats.ts --assemble-prompt <identity>`; that path composes stable doctrine, allowed rooms, research/archive capability guidance, Bifrost digest, a supplied Interpreter memory surface, and a supplied Interpreter conversation surface into the exact child prompt. It must not invent personality, mood, affect, or social memory from typed state. Situational obligations such as direct-call priority or first-public-introduction duty are emitted only when the scheduler packet proves they apply, not as standing prompt law. Until the first Spark/Codex Interpreter calls are wired into the live heartbeat, repo Face turns intentionally lack state/conversation-projected prose rather than using a fake substitute. The child character should receive projected memories, mood, obligations, allowed rooms, governance memory, and recent chat as prose rather than structured state. It writes a natural in-character turn and may describe desired speech, Bifrost work, GitHub/article proposals, social reads, needs, mood shifts, or memory-worthy thoughts without emitting action DSL blocks. The second Spark/Codex Interpreter decides route/retry/drop and translates warranted effects into structured `STATE NOTE`, `SAY`, and `BIFROST TOPIC` blocks for the worker. `STATE NOTE` blocks become typed short-term memory, affect needs, social bonds, status reads, mood dimensions, or agency pressure. The worker still accepts legacy compact JSON sentinels and legacy `UPDATE REQUEST` blocks for compatibility, but the live prompt path keeps transport and state syntax out of the character's roleplay surface.
- Repo character turns now have a state-projector plus turn-Interpreter shape. The state-in boundary is `prompts/repo-face-state-projector.prompt.md`: typed state and identity material are translated into lived character prose before the child sees it. The state projector must surface values, drives, wounds, fascinations, aesthetic taste, dignity, mood, needs, social reads, bonds, and agency pressure when the state contains them. Current-room and nearby-channel logs no longer cross a model conversation projector; deterministic code attaches raw oldest-to-newest transcript evidence with explicit recency/correction rules so the Face can notice human reframes directly. Deterministic code still owns full prompt assembly and inspection through `scripts/run-repo-face-heartbeats.ts --assemble-prompt <identity>`; that path composes stable doctrine, allowed rooms, research/archive capability guidance, Bifrost digest, projected memory/affect prose, and raw conversation evidence into the exact child prompt. It must not invent personality, mood, affect, or social memory from typed state. Situational obligations such as direct-call priority or first-public-introduction duty are emitted only when the scheduler packet proves they apply, not as standing prompt law. The child character writes a natural in-character turn on full `gpt-5.4` and may describe desired speech, repo/article proposals, social reads, needs, mood shifts, or memory-worthy thoughts without emitting action DSL blocks. The cheaper Spark/Mini parent Interpreter decides route/retry/drop and translates warranted effects into structured `STATE NOTE` and `SAY` blocks for the worker while governance dispatch remains paused. `STATE NOTE` blocks become typed short-term memory, affect needs, social bonds, status reads, mood dimensions, or agency pressure. The worker still accepts legacy compact JSON sentinels and legacy `UPDATE REQUEST` blocks for compatibility, but the live prompt path keeps transport and state syntax out of the character's roleplay surface.
- `scripts/run-repo-face-heartbeats.ts` now acts as the broader agent CTB/Final-Fantasy-X-style initiative scheduler. The wall-clock task is only a pulse; virtual initiative time chooses who gets the next turn. The scheduler stores per-agent speed, reaction bias, load, groups, heat, effective speed, next-turn time, active turn markers, and recent queue history at `REPO_FACE_HEARTBEAT_STATE_PATH`. Repo Faces queue `repo-face-rumination` owner-Codex jobs; built-in Void currently participates as the `void` system agent and starts the existing moderation rumination runner as its turn. Initiative freezes while an owner-Codex job or Void moderation lock is active; recovery is scheduled only after that active turn completes, and completion cannot grant another turn in the same scheduler pulse. The active-job scan recovers stale Face jobs by marking ancient approved/running rows failed through the job queue before building the active-turn map. Above-baseline heat bypasses repo Face nap gating, while active-turn freeze still prevents overlapping thoughts.
- The live wall-clock pulse for this scheduler is `scripts/run-gamecult-orchestrator.ps1`, installed as `GameCult Local Orchestrator`. `scripts/install-repo-face-heartbeat-task.ps1` remains available for isolated testing, but its per-organ scheduled task should stay disabled in normal operation. Turn frequency is controlled by `REPO_FACE_HEARTBEAT_BASE_RECOVERY_MINUTES`, `REPO_FACE_HEARTBEAT_GLOBAL_HEAT`, `REPO_FACE_HEARTBEAT_SPEED_OVERRIDES`, and `REPO_FACE_HEARTBEAT_HEAT_OVERRIDES`. Heat keys can target `all`, `kind:<participantKind>`, `turn:<turnKind>`, `identity:<id>`, `repo:<repo>`, `display:<name>`, `channel:<id>`, or the bare identity/repo/display key.
- The local ignored registry currently includes Nibu for `AetheriaLore`, with Face state in `E:/Projects/AetheriaLore/.voidbot/state/nibu.cc`, Discord role id `1505918221453103247`, and an Aquarium channel allow-list. Nibu's Face state now treats AetheriaLore-first map-building, abrasive character voice, consensus-backed change agency, her own `Aetheria/Lore/Nibu.md` character-note stewardship, Nibu-authored lore essays, and tangential setting fascination as separate active doctrines. That tangential fascination points at embodied ship minds, junkyard abandonment, murderous autonomy, salvage, and save-scumming survival. The repo Face heartbeat organ is now pulsed by `GameCult Local Orchestrator`; after restarting the stack onto the new worker code, forced heartbeat job `76020f1e-0c55-424b-9e35-89ba7f77fcc3` completed without base-bot auto-posting.

View file

@ -191,7 +191,7 @@ The runner can hand these to a CLI/MCP tool. The store decides what survives.
- recent event timing in that bounded context now crosses a projection boundary first, so the child sees "5 minutes ago" language while the parent keeps exact timestamps for bookkeeping
- public artifact sharing now crosses the same boundary: repo activity exposes content hints, rumination verifies authorship through retrieval, and a configured `publicSpeechTarget` lets Void queue a normal typed candidate intervention instead of needing a blog-specific announcement path. Void also has explicit herald jurisdiction over the GameCult website/blog; essay-shaped pressures should queue article-proposal candidates for Void-authored site pieces.
- Discord mention participation now uses role/display-name/bot addressing as CTB queue input and parent-owned speech rails as the output boundary. A Face mention appends a pending obligation for that Face; an ordinary Void mention or DM appends a pending obligation for the built-in `void` participant. Ambient direct mentions no longer create direct provider jobs.
- repo-scoped character turns reuse the typed self-state machinery per identity, but the child character must not see that machinery directly. Addressed chats are consumed by the heartbeat scheduler, which prioritizes the identity, and the intended boundary has three Interpreter prompts: `repo-face-state-projector.prompt.md` for state/context -> roleplay-friendly memory/mood/obligation/personhood prose, `repo-face-conversation-projector.prompt.md` for current-room and nearby-channel logs -> narrative conversational memory, and `repo-face-turn-interpreter.prompt.md` for natural character reflection -> typed memory, affect, social-read, mood, agency, speech, and Bifrost operations. Deterministic prompt assembly remains valid: `scripts/run-repo-face-heartbeats.ts --assemble-prompt <identity>` composes the final child prompt from templates, channel policy, repo/archive research guidance, Bifrost digest, and supplied Interpreter memory/conversation surfaces. It must not turn typed state or raw chat logs into character prose itself, and it should emit situational obligations only when the current packet proves them true. Until the first Interpreter calls are wired, the live turn prompt intentionally lacks projected memory/conversation prose rather than pretending an inspection renderer is the real architecture.
- repo-scoped character turns reuse the typed self-state machinery per identity, but the child character must not see that machinery directly. Addressed chats are consumed by the heartbeat scheduler, which prioritizes the identity, and the intended boundary has two model prompts: `repo-face-state-projector.prompt.md` for state -> roleplay-friendly memory/mood/personhood prose, and `repo-face-turn-interpreter.prompt.md` for natural character reflection -> typed memory, affect, social-read, mood, agency, and speech operations. Current-room and nearby-channel logs are not model-summarized; deterministic prompt assembly attaches raw oldest-to-newest transcript evidence with correction/recency rules so the Face must read the human exchange itself. The Face turn uses full `gpt-5.4`; the state projector and parent Interpreter use the cheaper Spark/Mini support chain. `scripts/run-repo-face-heartbeats.ts --assemble-prompt <identity>` composes the final child prompt from templates, channel policy, repo/archive research guidance, Bifrost digest, projected memory prose, and deterministic conversation transcript. It must not turn typed state into character prose itself, and it should emit situational obligations only when the current packet proves them true.
- repo-scoped Faces now have per-topic channel permissions instead of one work-shaped speech lane. `allowedChannelIds` remains the hard gate, while `channelPermissions` gives each Face labeled topics, speech thresholds, and speed multipliers. The current social invariant is that Aquarium is intentionally cheap for casual in-character chatter, while domain channels receive matching work/lore/music/realtime/agent-state thoughts when the recent cross-channel context lines up. Heat is the live-room valve: above-baseline heat now relaxes nap gating, but active-turn freeze still prevents an agent from being queued again while it is thinking.
- repo-scoped character prompts now explicitly treat social embodiment as durable state territory without exposing schemas to the child. A character may write naturally about humans, other agents, or room dynamics; the Interpreter turns meaning-bearing friendship, rivalry, irritation, trust, recurring bits, bonds, needs, and status reads into typed state when they carry claim/question, tension, action implication, and anchors. Public posts should speak to someone directly and must not begin with note-title formulas or identity labels such as "bright bridge note" or "tiny fish sorting note."
- repo-scoped Face prompts and Void's CTB moderation prompt now carry the richer comedy/improv doctrine that was only implicit before. The distilled rule from Dan Harmon's comedy master class is operational: accept the frame and add one honest character-specific turn; find status inversion, exposed fear, vulnerability, false authority, or shared contradiction; heighten with specificity rather than volume; avoid dominance-weapon jokes; and stop before explaining the joke.