Add Bifrost-backed Persona Reddit mouth

This commit is contained in:
Meta Vens 2026-06-22 14:45:49 +01:00
commit aade4f4980
9 changed files with 1119 additions and 13 deletions

File diff suppressed because it is too large Load diff

View file

@ -3107,6 +3107,7 @@ fn epiphany_mutation_contracts() -> Vec<CultNetDocumentMutationContract> {
"epiphany.persona_bubble_intent.v0",
"epiphany.character_turn_intent.v0",
"epiphany.discord_persona_post_intent.v0",
"epiphany.reddit_persona_post_intent.v0",
],
vec![
"epiphany.persona_bubble.v0",
@ -3114,7 +3115,7 @@ fn epiphany_mutation_contracts() -> Vec<CultNetDocumentMutationContract> {
"epiphany.character_turn_packet.v0",
],
vec![
"Persona bubble, draft, and Discord persona affordances are projected from typed Persona and character-loop artifacts.",
"Persona bubble, draft, Discord, and Reddit public mouth affordances are projected from typed Persona and character-loop artifacts; public crossings route through Bifrost.",
"Humans talk to Persona; sealed inner thoughts stay behind the projection boundary.",
],
),

View file

@ -323,8 +323,8 @@ remembers doctrine; the branch remembers the blade.
- The first Void-derived routine slice is landed in Epiphany, but VoidBot is reference lineage rather than a runtime dependency. `epiphany-heartbeat-store routine --store .\state\agent-heartbeats.msgpack --artifact-dir .\.epiphany-heartbeats --agent-store .\state\agents.msgpack` reads typed organ-state records, computes typed bounded memory resonance, maintains typed incubation themes/source coverage, runs typed analytic and associative cognition lanes, writes typed bridge syntheses/saturation/refractory cooling/decision state, produces typed candidate interventions, projects the active thought cluster through each role's organ/persona vectors, derives typed participant-local reactions from typed appraisals, applies personality/mood timing, advances typed sleep/dream-cycle state, updates the typed heartbeat store, and emits an auditable `epiphany.void_routine.v0` receipt. The routine now also carries anti-loop physiology imported from Void's calmer brain: `noveltyToSelf`, `noveltyToRoom`, source coverage, saturation pressure, refractory cooling, and explicit permission to let a live unsaturated thought deepen without forcing novelty theater every pass. This mutates only heartbeat physiology fields: project truth and role memory mutation remain on their reviewed surfaces.
- The next memory foundation must not build separate engines for repo graph and agent mind. Heartbeat cognition, role self-memory, repo architecture/dataflow, evidence scars, incubation, agency pressure, and context packets should converge through `EpiphanyMemoryGraph` profiles so one typed graph owns anchors, lifecycle, summaries, freshness, context cuts, and Qdrant embedding manifests. Repo, agent, and heartbeat profile producers now map accepted graph truth, reviewed organ-state record memories/goals/values, and provisional heartbeat short-term/incubation/agency/candidate pressure into memory graph documents without scanner/Qdrant/model work. Heartbeat import deliberately does not mark durable promotion. Compose now merges profile snapshots into one validated graph and rejects duplicate authority; `epiphany-memory-graph compose` writes a composed typed CultCache graph from typed source graph stores; `epiphany-memory-graph refresh` can invoke live agent-memory plus heartbeat-cognition producers from their typed stores into one graph store; `epiphany-core` has a native typed `EpiphanyThreadState` CultCache store seam; the bridge mirrors loaded Codex-thread Epiphany state into `state/thread-state.msgpack`; and CultNet now advertises logical `EpiphanyThreadState` plus `EpiphanyMemoryGraph` snapshot contracts. The next clean slice is local refresh/dogfood once a mirrored thread-state store exists, then a Qdrant embedding cache writer that treats embeddings as rebuildable manifests, not canonical memory.
- Manual Codex-run rumination still has an explicit aftercare rule until Epiphany owns the full sleep consolidator. In the intended Epiphany cycle, sub-agents ruminate when idle and distill when they sleep; in this supervising Codex thread, a heartbeat/routine vigil is only physiology until a separate closing pass reviews the receipts and decides whether map, handoff, evidence, or role self-memory should change.
- The Persona public-surface pass is landed as a bounded lane. Persona's organ-state record lives in `state/agents.msgpack`; `epiphany_specialists.toml` gives it a VoidBot-heartbeat-derived prompt stripped of moderation authority; `state/persona-discord.toml` and the native `epiphany-persona-discord` binary enforce that Persona may interact only through #aquarium. Missing channel id or token writes candidate chat artifacts instead of posting elsewhere. If a Persona has a persona name/avatar, `post` uses the shared Discord webhook pipe so each Epiphany can speak with its own nickname and avatar without a separate bot identity.
- Persona public speech now has a parent-side speech eligibility receipt at the mouth edge. `epiphany.persona_speech_audit.v0` remains the display artifact shape, and the durable witness is now `epiphany.cultmesh.persona_speech_audit.v0` in CultMesh with latest-key projection through local Verse context. The audit records action kind, content fingerprint, opening/topic keys, recent-window counts, repeated opening/topic counts, channel saturation, and allow/block reasons. `epiphany-persona-discord draft`, `bubble`, and `post` emit audits; `post` blocks repeated public openings before any Discord token/webhook work. Local MVP bubbles write that audit into the shared local Verse store. This is posting-edge policy, not a Persona prompt wish.
- The Persona public-surface pass is landed as a bounded lane. Persona's organ-state record lives in `state/agents.msgpack`; `epiphany_specialists.toml` gives it a VoidBot-heartbeat-derived prompt stripped of moderation authority; `state/persona-discord.toml`, `state/persona-reddit.toml`, and native `epiphany-persona-discord` / `epiphany-persona-reddit` binaries enforce that Persona's public mouth uses configured Bifrost crossings only. Discord posts route through Bifrost `discord-post`; Reddit posts route through Bifrost `reddit-post`. Bifrost owns GameCult identity, bridge policy, and receipts; Heimdall owns outside-provider OAuth/token custody and capability/account references. Missing channel, subreddit, bridge, or authority config writes candidate artifacts instead of posting elsewhere. Persona names, avatars, and Reddit flair are presentation metadata, not authority.
- Persona public speech now has a parent-side speech eligibility receipt at the mouth edge. `epiphany.persona_speech_audit.v0` remains the display artifact shape, and the durable witness is now `epiphany.cultmesh.persona_speech_audit.v0` in CultMesh with latest-key projection through local Verse context. The audit records action kind, content fingerprint, opening/topic keys, recent-window counts, repeated opening/topic counts, channel/subreddit saturation, and allow/block reasons. `epiphany-persona-discord draft`, `bubble`, and `post` plus `epiphany-persona-reddit draft` and `post` emit audits; public posts block repeated/saturated openings before any Bifrost transport work. Local MVP bubbles write that audit into the shared local Verse store. This is posting-edge policy, not a Persona prompt wish.
- The Void memory bridge is now native enough to keep the useful organs attached. `state/void-memory.toml` and `epiphany-void-memory` can check Void's Docker Postgres state spine, query Qdrant Discord-history and source/lore collections with the configured Ollama embedding model, and fetch raw archive context windows from Void's file-backed Discord archive. Native `epiphany-mvp-status` includes a `voidMemory` block so Aquarium/backend clients can inspect whether the mouth and memory are actually wired.
- The Epiphany-native character-loop now emits a Weksa/AquaSynth-ready utterance-state seam. `epiphany-character-loop turn --role Persona --stimulus <text>` loads Persona's typed organ-state record from `state/agents.msgpack`, builds typed projection/appraisal/reaction seeds from the shared heartbeat cognition documents, and emits an auditable `epiphany.character_turn_packet.v0` packet with `epiphany.agent_utterance_state.v0`. That utterance state is memory-free: identity, personality vectors, values, current mood, activation, 32 named affect dimensions, and the fixed 64-float `weksa.utterance_embedding_handoff.v0.1` character-state vector for AquaSynth/Weksa speech embedding. The same packet builder works for other role ids, because every organ already has a organ-state record.
- The heartbeat/Persona operator API pass is landed. Native `epiphany-heartbeat-store status` returns machine-readable initiative state plus CultCache store presence, thought appraisals, and derived reactions; native `epiphany-persona-discord bubble` writes Discord-independent `epiphany.persona_bubble.v0` artifacts, and native `epiphany-mvp-status` includes `heartbeat` and `Persona` blocks. Aquarium should call native/backend surfaces rather than resurrecting deleted Python action shims.
@ -563,10 +563,11 @@ For repo trajectory, personality, and memory birth packet changes, run:
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-repo-personality-smoke
```
For Persona's Discord mouth and Void memory bridge, run:
For Persona's Bifrost-backed public mouth and Void memory bridge, run:
```powershell
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-persona-discord -- smoke
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-persona-reddit -- smoke
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-void-memory -- smoke
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-void-memory -- status
```

View file

@ -0,0 +1,66 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://gamecult.dev/epiphany/cultnet/epiphany.reddit_persona_post_intent.v0.schema.json",
"title": "epiphany reddit_persona_post_intent v0",
"description": "Persona Reddit post intent for public organizing surfaces. Epiphany audits the speech and asks Bifrost to perform the Reddit crossing; Heimdall-backed capability/account references may be attached for outside-provider authority.",
"type": "object",
"additionalProperties": true,
"properties": {
"threadId": {
"type": "string"
},
"title": {
"type": "string"
},
"content": {
"type": "string"
},
"subreddit": {
"type": [
"string",
"null"
]
},
"personaName": {
"type": [
"string",
"null"
]
},
"personaFlairId": {
"type": [
"string",
"null"
]
},
"personaFlairText": {
"type": [
"string",
"null"
]
},
"bifrostIdentity": {
"type": [
"string",
"null"
]
},
"heimdallCapabilityRef": {
"type": [
"string",
"null"
]
},
"speechAuditId": {
"type": [
"string",
"null"
]
}
},
"required": [
"threadId",
"title",
"content"
]
}

View file

@ -705,6 +705,17 @@
"title": "epiphany discord_persona_post_intent v0",
"path": "epiphany.discord_persona_post_intent.v0.schema.json"
},
{
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.reddit_persona_post_intent.v0.schema.json",
"kind": "document_payload",
"wireContracts": [
"cultnet.schema.v0"
],
"schemaVersion": "epiphany.reddit_persona_post_intent.v0",
"documentType": "epiphany.reddit_persona_post_intent.v0",
"title": "epiphany reddit_persona_post_intent v0",
"path": "epiphany.reddit_persona_post_intent.v0.schema.json"
},
{
"schemaId": "https://gamecult.dev/epiphany/cultnet/epiphany.repo_startup_intent.v0.schema.json",
"kind": "document_payload",

View file

@ -46,16 +46,19 @@ initiative speed, reaction bias, cooldown multiplier, and birth mood pressure
in the typed heartbeat store. That seed is a one-time physiology nudge, not a
license to rerun personality initialization whenever the repo mood changes.
Persona's Discord boundary is still a small TOML configuration seam: it may
interact only through #aquarium. If the channel id is not configured, Persona must
write candidate chat artifacts through the native `epiphany-persona-discord draft`
Persona's public mouth is a Bifrost crossing, not local OAuth improvisation.
`state/persona-discord.toml` confines Discord speech to #aquarium and
`state/persona-reddit.toml` confines Reddit organizing posts to the configured
subreddit. If a channel, subreddit, bridge path, or authority reference is
missing, Persona writes candidate artifacts through the native draft commands
instead of posting. The public mouth does not invent new pulpits.
When a Persona has a configured `persona_name` and optional `persona_avatar_url`,
`epiphany-persona-discord post` uses the shared guild-channel webhook pipe so each
Epiphany instance can speak with its own nickname and avatar without needing a
separate Discord bot identity. The same boundary still applies: wrong channel
means draft, not improvisation.
`epiphany-persona-discord post` and `epiphany-persona-reddit post` audit speech
at the mouth edge, then ask Bifrost to perform the external crossing. Bifrost is
the GameCult identity provider and bridge receipt owner; Heimdall owns outside
provider OAuth/token custody and contributes capability/account references for
the crossing. Persona nickname, avatar, and Reddit flair are presentation
metadata only, not authority, voting power, or a separate platform identity.
Void memory access lives behind `state/void-memory.toml` and the native
`epiphany-void-memory` bridge. It checks Void's Docker Postgres state spine,
@ -75,6 +78,9 @@ cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-heartbeat-st
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-persona-discord -- draft --content 'Persona notices the organs are arguing about evidence again.'
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-persona-discord -- post --content 'Persona has entered the aquarium.' --persona-name 'Epiphany Persona'
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-persona-discord -- smoke
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-persona-reddit -- draft --title 'Epiphany status' --content 'Persona has a bounded public update.'
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-persona-reddit -- post --title 'Epiphany status' --content 'Persona has a bounded public update.'
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-persona-reddit -- smoke
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-void-memory -- status
cargo run --manifest-path .\epiphany-core\Cargo.toml --bin epiphany-void-memory -- search-history --query 'Epiphany Aquarium Persona Discord' --limit 3
```

Binary file not shown.

View file

@ -107,7 +107,7 @@ active_subgoals:
- The local wrapper now has an explicit repo-local Verse readback target for Gjallar/swarm-triage. `tools/epiphany_local_run.ps1` accepts `-LocalVerseStore` and `-LocalVerseRuntimeId`; operator intent/receipt documents still write to the operator store, while `gjallar` and `swarm-triage` read compact repo-work map rows from the requested store/runtime. Wrapper proofs `local-20260621-011151-541-70056545` and `local-20260621-011207-755-d65e5c7e` aimed at `.epiphany-smoke\repo-planning-brief-family-20260620-235939\repo-body\.epiphany\local-verse.ccmp` with runtime `repo-planning-brief-family-smoke` and printed real `REPO-WORK-MAP`, `REPO-WORK-MAP-SEMANTIC`, family/path/branch/stage/gate rows with `privateStateExposed=false`. This is sight targeting only; the wrapper does not register repo-work schemas, reinterpret rows, or gain operator, Mind, Bifrost, Idunn, scheduler, publication, deployment, or service authority.
- Gjallar/swarm-overview now projects typed repo-work overview history into the compact global sight report, Persona's public Eve surface lowers the same queue for peers, and Self has a first runnable queue surface over it. `epiphany-work overview` now also emits `epiphany.repo_work_intake_consensus_readback.v0` plus a compact `CONSENSUS` TUI row from the accept/plan receipt chain, so agents can see Persona/public feedback ids, Imagination consensus receipt route, public/candidate ref counts, model-authored safe family, and authority denials without opening private payloads. `epiphany-verse-query gjallar` reads all `epiphany.cultmesh.repo_work_overview.v0` event documents from local Verse, excludes the `latest` mirror, preserves latest scalar fields, emits multi-item `repoWorkOverviewRows`/TUI rows, and publishes bounded priority 35-39 non-mutating `repo-work-overview` action rows with `owner=Gjallar`, `hostedBody=repo-work`, `authorityGate=repo.work.overview`, `mutatesState=false`, `requiresElevatedAuthority=false`, proof-bundle refs, current gate/blocker/next-safe-move, and `privateStateExposed=false`. `epiphany-verse-query eve-surfaces`, `connect-eve`, and globally invokable Persona `eve-connect` tool readbacks expose compact `REPO-WORK-PEER` rows with item, gate, blocker, next safe move, branch, publication/sync state, and private seal for the public Persona surface only. `epiphany-work queue-run` selects current-repo rows at `ready-to-run`, `ready-to-adopt`, or `ready-to-execute`, delegates action to `tick`, refreshes overview after real advancement, and writes `epiphany.repo_work_queue_run_receipt.v0`; wrapper mode `tools/epiphany_local_run.ps1 -Mode repo-work-queue-run` exposes the same surface. Disposable proofs `.epiphany-smoke\gjallar-repo-work-queue-20260620-012027`, `.epiphany-smoke\eve-repo-work-queue-20260620-013322`, `.epiphany-smoke\queue-run-20260620-014314`, wrapper artifact `local-20260620-014627-174-aada0241`, and consensus readback smoke `.epiphany-smoke\repo-consensus-brief-family-20260621-005433` show queue sight, public Eve lowering, direct queue-run advancement, wrapper dry-run, and Persona-to-Imagination consensus readback without publication, merge, service lifecycle mutation, elevation, cross-repo mutation, or private-state exposure.
- Agent memory lifecycle now has a first typed v0 operation surface in `epiphany-core::agent_memory`: `AgentMemoryLifecycleOperation` can revise, retire, crystallize, prune, and merge bounded memory records after role/agent review, and `epiphany-agent-memory-store review-lifecycle|apply-lifecycle` exposes the path at the operator edge. This closes the first explicit lifecycle-operation slice from the swarm-readiness gate; it does not yet make sleep silently mutate identity or project truth.
- Persona public speech now has a parent-side eligibility receipt at the Discord/Aquarium mouth edge. `epiphany.persona_speech_audit.v0` records action kind, content fingerprint, opening/topic keys, recent-window counts, repeated opening/topic counts, channel saturation, and allow/block reasons. `epiphany-persona-discord post` blocks repeated public openings before token/webhook work; draft and bubble still emit audit receipts for operator inspection. The compatibility JSON artifact remains for display, but the durable witness is now `epiphany.cultmesh.persona_speech_audit.v0` in CultMesh with latest-key projection through local Verse context. Local MVP Persona bubbles now write that durable witness to `.epiphany-run/cultmesh/local-verse.ccmp`, not a separate mouth-side store. This is posting-edge policy, not a Persona prompt wish.
- Persona public speech now has a parent-side eligibility receipt at the public mouth edge. `epiphany.persona_speech_audit.v0` records action kind, content fingerprint, opening/topic keys, recent-window counts, repeated opening/topic counts, target saturation, and allow/block reasons. `epiphany-persona-discord post` and `epiphany-persona-reddit post` block repeated or saturated public openings before Bifrost transport work; draft and bubble paths still emit audit receipts for operator inspection. Bifrost owns Discord/Reddit crossing identity, bridge policy, and receipts; Heimdall owns outside-provider OAuth/token custody and supplies capability/account references for Bifrost identities. The compatibility JSON artifact remains for display, but the durable witness is now `epiphany.cultmesh.persona_speech_audit.v0` in CultMesh with latest-key projection through local Verse context. Local MVP Persona bubbles now write that durable witness to `.epiphany-run/cultmesh/local-verse.ccmp`, not a separate mouth-side store. This is posting-edge policy, not a Persona prompt wish.
- Grow the VoidBot repo-Persona pattern into Epiphany's product direction: each Epiphany owns one or more repos, has one or more Personas, and exposes project-native conversation through Aquarium, Discord, voice/WebRTC, stream surfaces, CLI, and CultNet clients so humans can talk to projects instead of pre-authoring complete implementation briefs. Persona is special because Imagination acts as Projector, turning typed state into narrative context before the Persona thinks, and Mind acts as Interpreter, turning natural Persona output into reviewed state/side-effect intents after the Persona thinks.
- Make Mind the gateway between thought and state. The state is the Mind: sub-agent outputs are worker thoughts, not authority. Role/reorient findings, statePatch, selfPatch, observations, evidence, receipts, scratch, and checkpoints must route through Mind review before durable state changes, using the VoidBot Persona Interpreter pattern as the starting membrane. Mind gateway reviews and Mind state-commit receipts are now typed runtime-spine documents for role/reorient acceptance; the review is persisted before state admission, and the commit receipt records the resulting durable revision after admission.
- Use `notes/mind-cultnet-contracts.md` as the authority map for Mind's CultNet contract surface. CultMesh Verse placement is part of the contract: `epiphany-internal` carries private thought/proposal/review/commit/rejection flow, `gamecult-local` carries trusted operator-safe sharing without private-state authority, and `epiphany-global` carries public thought weather plus adoption receipts only.

17
state/persona-reddit.toml Normal file
View file

@ -0,0 +1,17 @@
schema_version = "epiphany.persona_reddit.v0"
subreddit = "GameCultOrg"
persona_name = "Epiphany Persona"
persona_flair_text = "Epiphany"
bifrost_bridge_cli_path = "../Bifrost/tools/bifrost-bridge.mjs"
bifrost_identity = "epiphany.Persona"
bifrost_source_kind = "epiphany_persona_reddit"
bifrost_authority_ref = "epiphany.persona_speech_audit"
heimdall_capability_ref_env = "HEIMDALL_CAPABILITY_REF"
policy = [
"Persona may create Reddit organizing posts only through Bifrost reddit-post.",
"The subreddit is a public organizing surface, not a vote ledger.",
"Bifrost identity plus a Heimdall-backed capability/account reference should appear in bridge receipts for blessed crossings.",
"Persona flair identifies presentation voice; it does not grant authority or voting weight.",
"Persona must not expose raw transcripts, private notes, sealed artifacts, or local Verse state.",
]