From e16663f5cbb952ab341c683b413926903d63ec17 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Thu, 1 Oct 2026 22:31:13 +0200 Subject: [PATCH] Eureka self cut map r3 (Imagination): Epiphany is her body; one connector; GCLP attachments Co-Authored-By: Claude Opus 5.5 --- docs/eureka-self-cut.md | 1450 ++++++++++++++++++++++++--------------- 1 file changed, 884 insertions(+), 566 deletions(-) diff --git a/docs/eureka-self-cut.md b/docs/eureka-self-cut.md index 5a18b9d..ae130a5 100644 --- a/docs/eureka-self-cut.md +++ b/docs/eureka-self-cut.md @@ -1,11 +1,13 @@ # The Mind's own Self: map -Status: map, Imagination (`imagination-self`), revision 2 of 2026-10-01, +Status: map, Imagination (`imagination-self`), revision 3 of 2026-10-01, session `self-2026-10-01b`. Campaign `eureka-substrate` in Eureka's mind (instance `eureka`). Questions, cut specs, follow-ups and rulings are typed documents there; this page keeps body facts, the model page and rationale. Target: `docs/eureka-substrate-target.md` (`133324a7`). Rulings this revision -rests on: `eureka-substrate:ruling:operator-mind-runs-own-self`, +rests on: `eureka-substrate:ruling:operator-means-shape-ends`, +`:operator-eureka-adopts-epiphany-body`, `:operator-this-is-epiphany`, +`:operator-provider-portable-connector`, `:operator-mind-runs-own-self`, `:operator-mind-mission`, `:mind-self-burn-rate`, `:operator-persona-in-mind`, `:mind-rules-which-forks`, `:mind-self-merge-authority`, `:charters-into-eureka-repo`. @@ -22,7 +24,7 @@ The operator's words, in order. The first two were the first revision's law > I get it to work on the stuff I care about, like Aetheria. The Mind must > operate its own Self instance, with the goal of improving itself. -The second revision's law, in order: +The second revision's law: > I mean impersonation in the sense that there is no operational difference > between a work item dispatched on my request versus one dispatched on the @@ -39,672 +41,988 @@ The second revision's law, in order: > shape it can have is the agency machinery we already built with voidbot. Then > she can speak for herself on Discord, and act by her Self with Eureka. -And earlier: "A Persona is pointless without a Mind, that's why voidbot has been -on hiatus. It felt wrong to tell them they have agency and identity and then -make them dependent on me for dispatching the development work they were -inevitably obsessed with." +The third revision's law: -The first revision's design law still stands: the bloodhound observation -("RLVR-trained agents chase the objective in front of them, steering be -damned") means the Mind's Self is a second agent whose only objective is the -Mind, not a rule added to the operator-facing Self. The Persona ruling says -what that agent *is*: not a timer that prints a brief, but a Persona with -initiative pressure, memory and a mouth, whose scheduler already exists in -VoidBot, who holds her state in the Mind, and who dispatches her own Claude -Code Self when her pressure says so. The timer of revision 1 is demoted: the -waking organ is the Persona scheduler. +> I don't think this is voidbot anymore. This is epiphany. What is this if not +> epiphany? + +> Epiphany is supposed to be as provider portable as possible, just like +> Ghostlight, it gets it through a typed connector daemon and I want to +> continue that tradition. + +> I think you have the right of it. Eureka should adopt Epiphany as her body. + +> Look back at the Bifrost charter back when it was called GameCult Labor +> Platform or GCLP. See the /dossier page on the GameCult site, too. The means +> shape the ends. We shape the means with the agents we're building, with +> Epiphany and how we steward it. + +So: Eureka is the first Mind's name (instance `eureka`, forge user `eureka`, +the forge itself). Epiphany is her body and the open project. The faculty +method (pipeline, charters, briefs, tools; today the `GameCult/Eureka` repo) +is one of her organs, not a sibling product. Where this map says "Epiphany", +read "Eureka's body"; where it says "her", read Eureka. + +What stands from revision 1: the bloodhound observation ("RLVR-trained agents +chase the objective in front of them, steering be damned") means the Mind's +Self is a second agent whose only objective is the Mind, not a rule added to +the operator-facing Self. What stands from revision 2: the waking organ is a +Persona with initiative pressure, memory and a mouth, who dispatches her own +Eureka Self when her pressure says so; the timer is dead. What revision 3 +changes: the organism is Epiphany. Her resident Self (`epiphany-swarm`), her +native Persona (`epiphany-persona-service`, Projector, Persona, Interpreter), +her permit issuer and her typed Discord crossing are the body. VoidBot's +scheduler, Interpreter prompt, registry and heat dial are demoted to prior art +(B7). Model access goes through one typed connector daemon with Claude Code +print mode as one provider behind it (B13-B14). The Persona document in Huginn +stays the one Persona state; Epiphany reads and writes it (B15). Pinned heads for every fact below: | Repo | Ref | Commit | | --- | --- | --- | -| Huginn | `origin/main` | `bf73c42` (docs only over `a699630bbc49552fa747e9c0fabba24598fec77a`; crate line numbers below hold at both) | -| Epiphany | `origin/main` (= the leaf Huginn pins, `crates/huginn-mind/Cargo.toml:22`) | `ef9568655e75bb6cb362a038e2739a5ac5e777ee` | +| Huginn | `origin/main` | `bf73c42` (docs only over `a699630bbc49552fa747e9c0fabba24598fec77a`; crate line numbers hold at both) | +| Epiphany | local `main`, `F:\Projects\Epiphany` (clean) | `4006b14bf29b49410bd18b43acb4d42c3c4e2443` (2026-09-30). Huginn's leaf pin is `ef956865`; the two differ only in notes and the leaf's CultLib pin. | +| CodexConnector | `main`, `F:\Projects\CodexConnector` | `65192892f0ed829b8a228eabba44a4f3ba0f7268` (2026-09-04; frozen since) | +| Ghostlight | `main`, `F:\Projects\Ghostlight` | `eeb3a68` (2026-09-23) | +| Bifrost | `main` `F:\Projects\Bifrost`; release on Yggdrasil `cb3239aa-f67f5122` | `160fac2` (2026-10-01) | | gamecult-ops | `main` | `2aeb7364fffcd404aada28bfd30fb4e5c753cf53` | | Eureka skill | `main` (checkout `~/.claude/skills/eureka`) | `b260fbeec6d96ff06882ed9c37d3e1204c424e99` | | VoidBot | `main`, working tree `F:\Projects\VoidBot` (clean) | `46d891b` (2026-09-13) | -| Bifrost | `main` `F:\Projects\Bifrost`; release on Yggdrasil `cb3239aa-f67f5122` | `160fac20c98b70f3db9110099157d4dd720ac354` | -| Idunn | working tree `F:\Projects\Idunn` | (uncommitted-state unknown) | +| Idunn | `main` | `6667f0f` (2026-10-01) | -Probes of revision 1 ran from Starfire 2026-10-01 18:40-19:10 UTC; those of -revision 2 (B7-B10) 19:40-20:10 UTC (21:40-22:10 CEST). `lib.rs` is -`epiphany-pipeline/src/lib.rs`. The VoidBot facts come from the working tree -on disk and the `voidbot` MCP index (indexed 2026-08-30; disk wins), plus an -Eyes pass over the same tree (37 reads, nothing run). +Probes of revision 1 ran from Starfire 2026-10-01 18:40-19:10 UTC; revision 2 +(B7-B10) 19:40-20:10 UTC; revision 3 (B11-B16 and the re-checks in B3, B5) +after 20:15 UTC, from Starfire over `ssh ygg` (read-only; `sudo -n` only for +`ls`, `cat` of deployment records and `journalctl`). Repo facts for Epiphany, +CodexConnector and Ghostlight come from two Explore passes over the working +trees (nothing built, nothing run) and my own reads. `lib.rs` is +`epiphany-pipeline/src/lib.rs`. ## Body facts ### B1. The Mind (Huginn on Yggdrasil) -- `whoami` (revision 2): instance `eureka`, endpoint `rudp://10.77.0.1:17872`, - reachable, 403 documents, 128 receipts, index `Current`, epoch - `epiphany.pipeline.epoch.v2`. (Revision 1 saw 377 and 120.) +- `whoami` (revision 3): instance `eureka`, endpoint `rudp://10.77.0.1:17872`, + reachable, 422 documents, 133 receipts, index `Current`, epoch + `epiphany.pipeline.epoch.v2`. - `huginn.service` (probe: `systemctl cat`): user `huginn`, `--bind 10.77.0.1:17872`, hand-managed ("interim unit until Idunn owns it"). `ss -lunp` shows the socket only on the WireGuard address `10.77.0.1` (`wg-gamecult`). So the mind is reachable from Starfire over the mesh and - from Yggdrasil itself, and from nowhere else. A Claude cloud routine cannot - reach it. -- Campaigns in the mind (query): `idunn-watchdog` and `eureka-substrate`. + from Yggdrasil itself, and from nowhere else. +- Campaigns in the mind: `idunn-watchdog` and `eureka-substrate`. `eureka-substrate`'s repos are `GameCult/Huginn`, `GameCult/Epiphany`, - `GameCult/Eureka`, `GameCult/gamecult-ops` (campaign document); a campaign - has no resolution and no revision, so VoidBot and Bifrost cuts cannot be - `cut_spec`s under it. Its target r1 lists "Persona-state reading in Huginn" - under `not_in_scope`; the Persona ruling contradicts that line, so the - target needs an r2 (Self admits targets; the proposed text is in the + `GameCult/Eureka`, `GameCult/gamecult-ops`; a campaign has no resolution and + no revision, so a CodexConnector or Bifrost cut cannot be a `cut_spec` under + it (B16). Its target r1 lists "Persona-state reading in Huginn" under + `not_in_scope`; the Persona ruling contradicts that line (proposed r2 in the rationale). - Kinds: thirteen (`lib.rs:760-790`). None records a Self session, a claim on - a spec, a burn rate, Persona state or a landing (`gap-landing-untyped` is in - force). -- `RulingAuthority { Operator, Standing, Defaulted }` (`lib.rs:458`, in - `unit_enums!`). Huginn's selection alias domain repeats it - (`crates/huginn-mind/src/rows.rs:46`, `AUTHORITIES`), the published schema - repeats it (`schemas/cultnet/epiphany.pipeline.ruling.v2.schema.json:75-78`), - and the skill repeats it (`references/campaign-state.md:172`). A unit enum - variant added by name is a widening: stored rulings decode unchanged, which - the target's `stored-documents-valid` truth requires. (`value_types!` emits - no serde default on fields, so adding a *field* would not be; map - `eureka-substrate-map.md` B1. No cut here adds a field to an existing kind.) + a spec, a burn rate, Persona state or a landing (`gap-landing-untyped`). +- `RulingAuthority { Operator, Standing, Defaulted }` (`lib.rs:458`); a unit + enum variant added by name is a widening (map `eureka-substrate-map.md` B1). - `Faculty` has `Operator` and `SelfFaculty` (`rows.rs:42`); admission does not - check faculty (`gap-faculty-attribution`, in force). So nothing in the mind - today can tell the Mind's Self from the operator-facing Self except the - `session` label on the receipt. Follow-up - `operator-authority-needs-operator-credential` (in force) asks for a - credential the operator holds and the Mind's agent does not. -- Instance-rooted kinds are the pattern for a run record: `instance`, - `stewardship` and `hand_off` key off the instance (`lib.rs:913-946`), and - Huginn's admission rules for `hand_off` sit at - `crates/huginn-mind/src/admission.rs:231-262, 328-330`. Per-kind aliases are - declared at `rows.rs:362`. + check faculty (`gap-faculty-attribution`). Nothing in the mind today can tell + the Mind's Self from the operator-facing Self except the `session` label on + the receipt; follow-up `operator-authority-needs-operator-credential`. +- Instance-rooted kinds are the pattern for a run record (`lib.rs:913-946`; + Huginn admission for `hand_off` at `admission.rs:231-262, 328-330`). +- **The Rust client exists.** `crates/eureka-state` is a library plus a binary: + `HuginnClient { endpoint, instance, timeout }` with one method + `call(HuginnMindRequest) -> Result` over + `cultnet-rs` RUDP, resolving `Deferred` bodies (Huginn Cut 13a, merged + `33641f4`; `notes/huginn-organ-cut.md:447-456, 474-482`). Epiphany is Rust, + so it reaches the mind through this crate as a git dependency on Huginn. No + TypeScript client is needed by this design (revision 2's `huginn-ts-client` + is withdrawn). ### B2. Claude Code on Starfire: auth, headless mode, usage -- `claude --version`: 2.1.268. Binary - `%AppData%\npm\node_modules\@anthropic-ai\claude-code\bin\claude.exe`. -- Account (`~/.claude.json` `oauthAccount`, secrets not read): Max plan, - `billingType: stripe_subscription`, rate-limit tier `default_claude_max_20x`, - extra usage off. There is no API key and no per-token billing: every model - call the Persona or her Self makes spends the operator's subscription quota. - VoidBot's own providers (B7) cannot reach Claude at all. -- `~/.claude/.credentials.json` `claudeAiOauth`: an access token with - `expiresAt` 21:33:57 CEST, file written 13:33 CEST, so the interactive - access token lives eight hours and is refreshed by the interactive client. -- The failure the brief names, from this session's transcript - (`~/.claude/projects/F--Projects-CultLib/b471ff9b-….jsonl`): - `401 OAuth access token has expired. Re-authenticate to continue.` - (`authentication_error`), reported as "Agent terminated early due to an API - error" for subagents on `claude-sonnet-5-5`. A running subagent does not - survive the parent's token expiring. -- `claude setup-token`: "Set up a long-lived authentication token (requires - Claude subscription)". The binary carries the string "setup-token creates a - long-lived Claude.ai subscription token" and reads `CLAUDE_CODE_OAUTH_TOKEN` - (61 references). This is the headless credential for a subscription account: - no eight-hour expiry, no interactive refresh. Its lifetime and revocation are - the operator's (it is their subscription); not probed further because - creating one is the operator's act. -- **Demotion of a revision-1 claim.** Revision 1 said "never read by any - agent". The token reaches `claude` as an environment variable, and the agent - inside that process can read its own environment with one shell call. What - protects the credential is the reach of the user it runs as (B4), not - secrecy from the agent. Follow-up `persona-credential-in-process-env`. +- `claude --version`: 2.1.268. Account (`~/.claude.json` `oauthAccount`, + secrets not read): Max plan, `billingType: stripe_subscription`, rate-limit + tier `default_claude_max_20x`, extra usage off. No API key: every model call + the Persona or her Self makes spends the operator's subscription quota. +- The interactive OAuth access token lives eight hours + (`~/.claude/.credentials.json` `expiresAt`); a running subagent does not + survive the parent's token expiring (`401 OAuth access token has expired`, + this session's transcript). +- `claude setup-token` makes a long-lived subscription token read from + `CLAUDE_CODE_OAUTH_TOKEN` (61 references in the binary). This is the + headless credential. Its lifetime and revocation are the operator's. +- The token reaches `claude` as an environment variable, so the agent inside + that process can read it with one shell call. What protects it is the reach + of the user it runs as, not secrecy from the agent (revision 2 demotion). + Revision 3 narrows where it lives: the connector daemon's user (B14) and the + Self-run unit's user (B12), never Epiphany's own user. - Print mode: `claude -p` with `--output-format json|stream-json`, `--json-schema`, `--agents `, `--mcp-config`, `--allowedTools`, - `--permission-mode`, `--max-budget-usd ` ("Maximum dollar amount to - spend on API"), `--bg`, `--resume`, `--fallback-model`. The `json` output - carries the run's cost (`total_cost_usd`) and usage. So a headless run has a - structural spend cap and a measured spend, per run, without any rule in a - prompt. This is the only Claude path a Node process on Yggdrasil has. -- Plan usage as data: the desktop app's `get_usage` returned, at 18:43 UTC: - 5-hour window 2%, weekly all models 46% (resets 2026-10-05 22:00 UTC), - weekly Fable 0%. That tool exists only inside the desktop app. The CLI - binary knows no usage endpoint (`api/oauth/files/`, `api/oauth/organizations/` - only); it reads `anthropic-ratelimit-unified` response headers (44 - references), which a headless wrapper does not see. **There is no typed - reading of plan usage reachable from Yggdrasil.** Follow-up - `gap-usage-not-typed`. -- Local scheduled tasks exist in the desktop app - (`~/.claude/scheduled-tasks/streampixels-m1-checkin`, disabled, last run - 2026-09-18): a cron inside the desktop app on Starfire. They run only while - the app is open on the operator's workstation, which the load budget - (memory `workstation-load-budget.md`: one job at a time, nothing unannounced) - and "no overnight burns" (memory `token-budget.md`) both argue against for a - recurring unattended agent. Cloud routines (`schedule` skill) run off-mesh - (B1) and hold no forge key (B6): dead for this purpose. + `--permission-mode`, `--max-budget-usd `, `--bg`, `--resume`, + `--fallback-model`. The `json` output carries `total_cost_usd` and usage. The + Claude Agent SDK (Ghostlight's sidecar, B13) exposes the same: per-model + `modelUsage`, `total_cost_usd` as a client-side estimate, and a + `MaxBudgetUsd` fault, so a per-request spend cap is enforced by the provider + child itself. +- Plan usage as data: readable only through the desktop app's `get_usage` + (`gap-usage-not-typed`, in force). The CLI binary knows no usage endpoint. + **There is no typed reading of plan usage reachable from Yggdrasil.** The + connector's cost field (B14) is an estimate summed per caller, not the plan. +- Local scheduled tasks and cloud routines: dead for this purpose (revision + 2: workstation load budget, no overnight burns, off-mesh, no forge key). -### B3. Yggdrasil as the waking host +### B3. Yggdrasil: what runs, what is dead, and whose body it is -- No Claude Code installed (`claude: command not found` for `gamecultadmin`; - no `~/.claude`). `node` v24.14.1 and `npm` present, so `npm i -g - @anthropic-ai/claude-code` under a dedicated user is the install. No user - `eureka-mind` exists yet (`id` fails); `/srv/eureka` exists (the forge). -- Load: 16 cores, 62 GiB, load average 1.4, 54 GiB free. The verify stopgap - runs at most 5 jobs at 4 CPUs and 6 GiB each (`tools/stopgap/ygg-verify.sh` - header). A Claude Code process is light; its Hands and Soul push their - heavy work into the same stopgap containers they use from Starfire. The - stopgap pushes to `~/eureka-verify` over `ssh ygg`; run on Yggdrasil itself - that alias must resolve to localhost for the unit user, or the script gets - a local mode. Recorded in the wake cut's `first`. -- Non-resident workloads already run here hand-installed, outside Idunn - (`systemctl list-timers`): `bifrost-watchdog-notice.timer` (every minute, - oneshot, user `bifrost-notice`: system user, no shell, no home, credentials - through `EnvironmentFile=/srv/bifrost/env/persona-delivery.env` root 0600, - release under `/srv/bifrost/watchdog-notice/releases/-` with a - `current` symlink, `StateDirectory` for journal and receipts), - `gamecult-state-backup.timer` (nightly), `voidbot-source-refresh.timer`. - The runbook `bifrost-watchdog-notice-yggdrasil.md` calls this "an interim - root action on Yggdrasil … until Idunn owns non-resident workloads", and - ruling `idunn-watchdog:ruling:reader-deploy-owner` chose - `ops-hand-install`. **That is the precedent the Persona's unit follows**, - with the same deletion line. -- Idunn: `TargetDeclaration.service: ServiceDeclaration` is a required field - (`src/deployment.rs:33`), so a recipe without a resident service does not - parse. Follow-up `idunn-watchdog:follow_up:idunn-non-resident-workloads` - (in force) is exactly this gap; Idunn-managed units on the host today are - ghostlight, odin, streampixels-service and streampixels-web. -- Epiphany's own resident Self exists as prior art and is dead: units - `epiphany-swarm.service` ("Epiphany Yggdrasil resident Self"), - `epiphany-heartbeat.service` and `epiphany.service` are `inactive dead`. The - binary (`epiphany-core/src/bin/epiphany-swarm.rs`) runs a 60 s maintenance - loop over `ResidentSelfState`, `ResidentSelfPolicy`, pressure and typed work - obligations, on the Codex spine (`epiphany-openai-codex-spine`, - `codex-connector`). Its doctrine (`epiphany_doctrine.md`): "Heartbeats are - paced scheduling physiology, not cognition … When no typed work obligation - is active, the swarm sleeps. Heartbeat does not ruminate, dream, mutate - memory, or manufacture work." Codex left as supplier 2026-09-05 (memory - `codex-retirement.md`). The Epiphany Persona permit service that - `bifrost-persona-mouth.service` expects on Starfire (`10.77.0.2:17877`) is - not listening either (`netstat`: only 17875 and 17878 on localhost). So two - native Persona bodies are dead; the operator's ruling names VoidBot's, not - Epiphany's. +`systemctl list-units --all` for `epiphany* codex* ghostlight* huginn* +bifrost*` (revision 3): + +| Unit | State | Note | +| --- | --- | --- | +| `huginn.service` | active | the mind (B1) | +| `bifrost.service`, `bifrost-persona-feedback.service` | active | Bifrost alpha; the Persona-feedback provider (user `bifrost-feedback`) | +| `bifrost-persona-mouth.service` | **active** | "Bifrost authenticated Epiphany Persona Discord mouth" (B5) | +| `bifrost-watchdog-notice.timer` | active | once a minute | +| `epiphany.service`, `epiphany-swarm.service`, `epiphany-heartbeat.service` | dead (enabled) | the Aug 22 body (below) | +| `epiphany-model-connector.service` | dead (disabled) | Epiphany's first connector, Aug 24 (B14) | +| `codex-connector.service` | dead (disabled) | the standalone connector, last alive 2026-09-05 21:51 UTC, stopped on a Codex `401 token_expired` (journal) | +| `ghostlight-dungeon.service` | dead | its unit still names `GHOSTLIGHT_MODEL_PROVIDER=codex-connector` at `127.0.0.1:4103`; its code no longer opens that lane (B13) | + +- **The deployed Epiphany body is older than the repo.** `/srv/epiphany/app/current` + (Aug 22) holds 18 binaries, among them `epiphany-daemon-supervisor`, + `epiphany-heartbeat-store`, `epiphany-model-runtime`, + `epiphany-persona-discord-permit`, `epiphany-persona-feedback-ingress`. Head's + `Cargo.toml:60-100` builds nine: `epiphany-release`, `epiphany-state`, + `epiphany-repository-body`, `epiphany-swarm`, `epiphany-persona-service`, + `epiphany-persona-discord-permit`, `epiphany-mvp-coordinator`, + `epiphany-openai-runtime` (the model runtime bin) and the tool runtime. The + heartbeat scheduler authority was deleted on 2026-08-23 (`4d0db2c7`), the + semantic projector on the same day (`856648de`), and `epiphany_specialists.toml` + with "fictional contracts and dead prompt surfaces" (`ab3cacfb`). So + `epiphany.service` (supervisor) and `epiphany-heartbeat.service` cannot be + rebuilt from head: they are dead units with no source, and the deletion line + of this map removes them. `epiphany-swarm.service` can be rebuilt; its unit + ran `--model-provider openrouter --model stealth/ox-alpha` with an OpenRouter + key via `LoadCredential`, `--codex-home`, `--max-steps 1`, + `--cooldown-seconds 120 --idle-sleep-seconds 15`, and read Bifrost Persona + feedback from `/srv/bifrost/persona-feedback/outbox/deliveries.cc`. Its last + journal (Aug 22 17:46) reports `status: braked` and the wake authority + quoted in B11. +- Users that exist: `epiphany` (988, home `/var/lib/gamecult/epiphany`), + `epiphany-model` (984), `codex-connector` (983), `huginn`, `voidbot`, + `bifrost-feedback`, `bifrost-notice`, `ghostlight`. **No `eureka-mind`.** + Group `gamecult-model` holds the connector keys (`/etc/gamecult/codex-connector/ + ghostlight.key`, `epiphany-capstone-17.key`, root:gamecult-model 0640). +- Idunn: targets under `/var/lib/gamecult` include `epiphany`, + `epiphany-model-connector`, `codex-connector`, `huginn` (state root only; the + unit is interim). `/etc/gamecult/epiphany` holds the Bifrost feedback anchor, + an empty `mcp.toml` and a root-only `credentials/`. The Idunn recipe for + Epiphany is the Aug body's (`/srv/odin/deploy-manifests/epiphany`, + `inventory.md:775-788`); it does not describe head. `TargetDeclaration.service` + stays required (`Idunn src/deployment.rs:33`), which is fine here: every + Epiphany organ in this map is a resident service. +- Load: 16 cores, 62 GiB, load average 1.4. The verify stopgap caps 5 jobs at + 4 CPUs and 6 GiB. `node` v24.14.1 and `npm` present; no Claude Code installed. +- `127.0.0.1:18080` listens (Ghostlight's local lane, Bonsai 2 on Raven); the + Qdrant/Ollama facts of `huginn-organ-cut.md` P1-P3 hold. ### B4. The forge and the Mind's identity -- `GameCult/Eureka` on `https://eureka.gamecult.org/forge/`: public, default - branch `main`, pull requests on, **no branch protection** (API - `branch_protections` → `[]`, with the admin token, on the host). Ruling - `mind-self-merge-authority` requires protection that forbids direct push to - main for everyone; nothing enforces it yet. -- Team `minds`: `write` on `repo.code`, `repo.pulls`, `repo.actions`, - `repo.issues`, `repo.releases`, `repo.wiki`, all org repos. Not admin. -- User `eureka` exists since 2026-10-01T18:49:53Z, not admin, one key - `eureka@starfire` SHA256:`zcp0m+jV4AUXzGeoh9z+vfJS8xKHo9S865xOK8p6MGM`, which - matches `~/.ssh/eureka_mind_forge_ed25519.pub` on Starfire. The script - `provision-forge-minds-eureka` adds one key per host, so a Yggdrasil-run - agent gets its own key there. The handoff records the Starfire checkouts - now push and commit as `eureka` and `eureka-admin` holds no keys - (`F:\Projects\HANDOFF-eureka-self-2026-10-01.md`, "Waiting on the - operator"); revision 1's follow-up `forge-push-as-eureka` is done by that - record. -- The charters are not yet in the Eureka repo (`ls ~/.claude/skills/eureka`: - `LICENSE README.md SKILL.md docs references tools`; no `agents/`). Ruling - `charters-into-eureka-repo` moves them; that cut is not specified here (it - is an Eureka-repo cut whose anchors are the Starfire `~/.claude` files, and - Self owns it as the operator-facing side). -- Reach is the scope boundary, by construction: a Linux user on Yggdrasil - with the `eureka` forge key, checkouts of Huginn, Epiphany, Eureka and - gamecult-ops (and VoidBot, under option A of `persona-body-shape`), and no - GitHub credential, no SSH to Starfire, no Aetheria checkout, cannot touch an - operator project. No prompt rule is needed for "never touches Aetheria"; - the key ring is the rule. Huginn and Epiphany are on GitHub today: the wake - cut records how the Mind's user fetches them read-only and pushes branches - to the forge; GitHub stays the operator-facing Self's. +Unchanged from revision 2: `GameCult/Eureka` on the forge has no branch +protection yet (ruling `mind-self-merge-authority` needs it); team `minds` has +write; user `eureka` exists with one key `eureka@starfire`; +`provision-forge-minds-eureka` adds one key per host; the charters are not yet +in the Eureka repo. Reach is the scope boundary: a Linux user on Yggdrasil +with the `eureka` forge key, checkouts of the Mind's repos, no GitHub +credential, no SSH to Starfire, cannot touch an operator project. Revision 3 +adds: the Linux user that already owns Epiphany's state on the host is +`epiphany`; under `operator-eureka-adopts-epiphany-body` she is Eureka on +every surface that names the Mind (instance, forge user, Discord display +name), and `epiphany` names her body (Linux user, runtime id +`epiphany-yggdrasil`, Persona agent id `epiphany.Persona`, which Bifrost +checks as a constant in `persona-feedback.mjs:90`). The one open naming +question is the Discord display name (question `her-name`). +- Huginn and Epiphany are on GitHub today; the forge (`eureka.gamecult.org`) + is hers. Ruling `mind-self-merge-authority` wants branch protection on the + forge for every repo she merges into, so her body's repos need the forge as + their canonical remote before she lands code in them (follow-up + `body-repos-on-forge`). ### B5. The operator's channel and the Persona's mouth -- Bifrost `tools/bifrost-bridge.mjs discord-dm --recipient-id --content - `: DMs through Bifrost's bot token, exits 75 when it knows nothing was - sent (retry with the same nonce). `discord-post --channel-id - --content --persona-name [--persona-avatar-url ]` - (`bifrost-bridge.mjs:399-450`) posts **as a named Persona through a - per-channel webhook** that the bridge creates and caches - (`:821-845`; `BIFROST_DISCORD_PERSONA_WEBHOOK_URL_` overrides, - `:924`). The token is `BIFROST_DISCORD_BOT_TOKEN` or `DISCORD_BOT_TOKEN` - (`:401`). On Yggdrasil `/srv/bifrost/env/persona-delivery.env` (root 0600) - sets exactly `BIFROST_DISCORD_BOT_TOKEN` and `DISCORD_OWNER_ID` (names - read with `sed`, values not), and the bridge CLI is installed at - `/srv/bifrost/persona-feedback/runtime/current/Bifrost/tools/`. **So the - Persona's mouth exists on the host today with no new code**: one CLI call - under that env file. The VoidBot worker's own post path - (`postDiscordViaBifrostCultMesh`, `packages/core/src/bifrost-discord-command.ts:39`) - reaches the same bridge through a CultMesh command whose default URI is - `cultmesh://asgard.starfire.bifrost/commands/discord-post` and whose store - defaults to `E:/Projects/Bifrost/.bifrost/provider-store.cc` (VoidBot - `config:134-135`): a Starfire-era route, dead on Yggdrasil. -- `tools/watchdog-notice.mjs` is a one-shot reader that turns a typed store - (Idunn's `incidents.cc`) into owner DMs with a journal, receipts and retry. - Revision 1 proposed the same program over Huginn as the review channel; - with the Persona speaking for herself that reader is no longer the primary - channel (question `persona-report-channel`). -- Inbound: a human addressing the Persona on Discord reaches her today only - through VoidBot's Discord bot (`apps/bot`, gateway, attention-command - inbox) or Bifrost's persona-feedback observation path, which depends on - VoidBot observing (`runbooks/bifrost-persona-feedback-yggdrasil.md:4`). - Neither runs (B7). Follow-up `persona-inbound-mentions`. +Revision 2 mapped the bridge CLI `discord-post` as her mouth. Revision 3 finds +the typed mouth live on the host and demotes the CLI path to its transport leg. + +- `bifrost-persona-mouth.service` (user `bifrost-feedback`, active, requires + `bifrost-persona-feedback.service`) runs + `tools/persona-feedback.mjs serve-persona-delivery` over the single-writer + crossing stores `/srv/bifrost/persona-discord-crossing/requests.cc` and + `receipts.cc`, with `--epiphany-runtime-id epiphany-starfire`, + `--epiphany-persona-mouth-trust-anchor starfire-mouth-request-anchor.ccmp`, + `--epiphany-persona-permit-trust-anchor starfire-permit-anchor.ccmp`, + `--epiphany-persona-permit-rudp rudp://10.77.0.2:17877`, listening on + `rudp://10.77.0.1:17876`, reading `persona-delivery.env` (bot token). The + anchors are root-owned, dated Aug 8; the stores were last written Aug 8. + `inventory.md:760-771`: the production binding is the GameCult Aquarium + channel `1501196543150264332`, runtime `epiphany-starfire`, Persona agent + `epiphany.Persona`; "restarting it would arm the Persona crossing". +- The contract (Bifrost `tools/persona-discord-delivery-documents.mjs`, + `persona-discord-delivery.mjs`, `persona-discord-rudp.mjs`; Epiphany + `persona_discord_crossing.rs:32, 294-319`, `persona_discord_permit.rs`): + Epiphany signs `epiphany.persona_discord_delivery_request.v0` + (`requestId, effectDocumentId, targetRuntimeId, personaAgentId, channelId, + replyToMessageId, content, contentSha256, issuedAt, expiresAt, + privateStateExposed, signerIdentityId`) as source role `epiphany-persona-mouth` + on RUDP connection id `0xe91f_0003`; Bifrost admits it only when the + envelope's `sourceRuntimeId` equals its configured runtime id and the anchor + tuple (`gamecult.service_trust_anchor.v1`, service `epiphany-persona-mouth`, + runtime id, ed25519 key, purpose, signed schema, `bindingAuthority: root`) + verifies; it then asks Epiphany's permit issuer + (`bifrost.persona_discord_delivery_permit_request.v0`, connection id + `0xe91f_0002`, source runtime `bifrost-discord-yggdrasil`) and posts only + with a fresh `epiphany.persona_discord_delivery_permit.v0` (lifetime at most + 5 s, bound to request digest, nonce and the canonical CultMesh brake + document digest; issued only while the brake is `released`). The post is + the bridge CLI `discord-post --identity epiphany.Persona --source-kind + epiphany_persona_speech` with a crossing receipt. Receipts are the only + delivery proof; `unknown` is terminal. Full authority maps: + `notes/persona-discord-permit-authority-map.md`, + `notes/persona-conversation-authority-map.md`. +- **So her mouth exists with no new code on either side.** What is missing is + binding: the mouth unit is anchored to a Starfire runtime whose permit port + (`10.77.0.2:17877`) is not listening (`netstat` on Starfire: only 17875 and + 17878). Moving her to Yggdrasil means new anchors for runtime + `epiphany-yggdrasil` (Bifrost's own test fixture already uses that id, + `tests/persona-discord-delivery.test.mjs:13-35`), a permit issuer bound on + `10.77.0.1`, and the mouth unit's three flags changed: a root action in + gamecult-ops, no Bifrost code. The channel stays the operator's to name + (question `mouth-binding`). +- The brake is a separate control from the dial: binary, operator-released, + gating every external consequence (permit) and every resident Self cycle + (`epiphany-swarm.rs` cycle step 2). The dial scales cadence and run size; the + brake stops. Both stay. +- `tools/watchdog-notice.mjs` (owner DMs from a typed store) and the raw + `discord-post` webhook CLI remain available but are not her mouth: a second + path that bypasses the permit would put her speech outside the brake. +- Inbound: `bifrost-persona-feedback.service` already turns Discord events + addressed to the bound Persona into signed `bifrost.persona_feedback.delivery` + documents in the outbox, which `epiphany-swarm` imports as pressure (cycle + step 2) and which the conversation map lowers into Persona turns with reply + targets. The Bifrost-native ingress the conversation map's cut line 1 asks + for is the live `serve-discord` path (`persona-feedback.mjs:96-99`, + `bifrost.discord.persona_conversation_event.v0`). What is unbuilt is the + Epiphany side of mention obligations at head (follow-up + `epiphany-inbound-mentions`, superseding `persona-inbound-mentions`). ### B6. Scheduling today, and what the skill already derives -- The skill's recipes (`campaign-state.md` "Recipes") already derive the - queue: specs with no report (minus those blocked by a question raised in - them), reports with no verdict, open findings by severity, open questions and - follow-ups, substrate gaps. The operator-facing Self reads them at rehydrate - and then takes direction from the operator and from prose handoffs - (`F:\Projects\HANDOFF-eureka-self-2026-10-01.md` is one). -- What the queue cannot say: (1) who is working a spec right now (two Selves - would both take the first unreported spec); (2) whether a holding verdict - landed (`gap-landing-untyped`); (3) which of fifteen follow-ups comes first - (no priority, no order); (4) how active the Mind may be; (5) which Self - admitted a ruling. (1) and (5) are what the `run` kind carries; (4) is the - dial (B8). (2) stays its own gap. (3) is deliberately not a field: the - Persona orders her Self's work by the fixed rule in the rationale, and a - follow-up that must jump the queue becomes a cut spec. +Unchanged: the recipes derive the queue (specs with no report, reports with no +verdict, open findings, open questions and follow-ups, gaps). What the queue +cannot say: who is working a spec now; whether a holding verdict landed; +follow-up order; how active the Mind may be; which Self admitted a ruling. The +`run` kind (spec `cut-run-kind.r1`) carries the first and last; the dial is +the fourth; `gap-landing-untyped` stays. Revision 3 adds where the queue is +read from: Epiphany's resident Self reads it through `HuginnClient` each cycle +as its pressure source (B11, B12). -### B7. VoidBot's Persona machinery: what it is, what it needs, where it runs +### B7. VoidBot: what is borrowed, what is demoted -Source: `F:\Projects\VoidBot` at `46d891b`; the Eyes pass; `ssh ygg`. +Revision 2's B7-B8 facts stand as recorded (scheduler, initiative engine, +Interpreter prompt, `gamecult.persona_state.v0` reader and one-shot migration, +`globalHeat` dial, nothing running, paused since 2026-07-16). Under +`operator-this-is-epiphany` the demotions are: -- **Shape.** `apps/persona-scheduler/src/index.ts` is a resident Node process: - `loadConfig()`, an immediate tick, then a `setTimeout` loop every - `REPO_FACE_HEARTBEAT_INTERVAL_MINUTES` (default 5; Yggdrasil ran 1). Each - tick (`persona-scheduler-runner.ts`) is gated in order by: the pause file - `/state/agent-swarm-paused.json` (`control-source.ts:12-24`; missing - means running, unparseable means paused); `REPO_FACE_HEARTBEATS_ENABLED` - (default false); then it loads the identity registry, reads each Persona's - state observation (rest/nap), applies `globalHeat` (B8) and - `baseRecoveryMinutes` (default 10), idle cooling from Discord activity - (needs the bot token), semantic pressure (needs Qdrant and an embedder), and - selects ready participants (`initiative-engine.ts:498-525`): mentioned ones - always, **at most one unprompted per tick**, and only if its initiative - clock is due, its response pressure crosses the threshold with unconsumed - evidence, and `nextUnpromptedTurnAllowedAt` has passed. This is the CTB - initiative engine the handoff describes ("`initiative-engine.ts` owns - participant reconciliation plus clock, active-turn freeze/recovery …"; - `notes/fresh-workspace-handoff.md:148`). -- **A turn.** `turn-actuator.ts:9` submits a job `repo-face-rumination` to a - Postgres `JobQueue` (`createJob`, `initialState: "approved"`). `apps/worker` - executes it (`index.ts:493`, `executeRepoFaceJobWithInterpreter`). The - model provider is one of `owner_codex | openai_api | local_llm` - (`packages/shared/src/index.ts:21`); default `owner_codex` (`codex exec`, - `owner-codex-provider.ts:405-420`); Yggdrasil's last live `.env` used - `openai_api` with `mimo-v2.5-pro` (handoff `:199`). **There is no Anthropic - or Claude provider** (grep over apps, packages, scripts: nothing). The - Codex runtime spawned a CLI and parsed JSON lines from its stdout - (`owner-codex-runtime.ts:81-130`): the exact shape a `claude -p - --output-format stream-json` actuator takes. -- **What a turn can do.** The Face writes naturally; the Interpreter prompt - (`prompts/repo-face-turn-interpreter.prompt.md`) converts it into blocks: - `INTERPRETATION`, `STATE NOTE` (memory|need|bond|status|mood|agency), `SAY`, - `ARTICLE`, `REDDIT THREAD`; line 50: "Do not emit governance or dispatch - blocks." The worker executes at most one consequential action per turn - (proposal PR > PR comment > article > Reddit > Bifrost topic > update - request, `index.ts:582-596`), gated by `REPO_FACE_GITHUB_ACTIONS_ENABLED` - and `REPO_FACE_BIFROST_ENABLED` (both default false), and posts one `SAY` - only if nothing heavier fired (`:599-606`). Up to four `STATE NOTE`s go - through `applyVoidSelfStateOperation` into the Face's `.cc` (`:1990-2127`). - **No action dispatches development work**: nothing spawns Claude, Codex - or a workflow from a turn; `WORK`/`DISPATCH` does not exist. The worker's - MCP tool surface for a turn (`mcp-server-tools.ts`, 21 tools under - `registerIfAllowed`) is retrieval, Odin interface, Discord posting, Face - state read/apply, shared documents and `notify_owner`. -- **State.** Two families. (a) Void self-state: types `void.self_profile`, - `void.thought_memory`, `void.agency_pressure`, `void.face_affect`, … - (`packages/core/src/void-self-state-domain.ts:5-12`) at - `identity.faceStatePath` or `/private/repo-faces/.cc`. - (b) `gamecult.persona_state.v0`: a single global CultCache document - (`persona-state-domain.ts:83-88`, zod types), read by - `loadGamecultPersonaState` (`persona-state-service.ts:9-15`) from - `identity.personaStatePath` for `native_persona` identities only - (`persona-state-source.ts:78-81`, `.cc` only), projected to prompt text by - `persona-standard-state-projector.ts:3-33`. **Its only writer is the - one-shot migration** `migrateCanonicalPortablePersonaState` - (`persona-state-service.ts:58-68`, refuses an existing target, refuses - non-canonical provenance). A `STATE NOTE` at turn time writes Void - self-state, never `gamecult.persona_state.v0`. `persona:export-friendly` - is a YAML projection (`docs/persona-intake/`), `persona:migrate-portable` - the one-shot CLI, dry-run by default. Huginn's own legacy repo Persona - lives at `F:\Projects\Huginn\.voidbot\state\huginn.cc` (Void family; - `AGENTS.md:43` forbids touching it; `README.md:88-93`). Eureka has no - `.voidbot/` and no registry entry anywhere. -- **Identity.** The registry is `.voidbot/private/repo-discord-identities.json` - (ignored, not on Starfire's tree); fields per identity: `id`, - `identityKind: repo_face | native_persona`, `repoName`, `displayName`, - `repoPath`, `roleId`, `allowedChannelIds`, `channelPermissions`, - `avatarUrl`, `faceStatePath`, `personaStatePath`, - `remotePersonaFeedbackTarget` (`epiphany-identities.ts:120-143`). The - notes list native Personas (Muninn, Sleipnir, Hermodr, Tengu, Metame) and - repo Faces (Aqua, Nibu, Mimir, Void, Epiphany, Libby, Huginn, …); none is - Eureka or "the Mind". -- **Where it runs: nowhere.** `voidbot.service` on Yggdrasil - (`systemctl cat`) runs only the compose service `swarm-publisher` (the Eve - dashboard publisher, container `voidbot-swarm-swarm-publisher-1`, up 2 - days); the compose file `/srv/compose/voidbot.yggdrasil.yaml` defines no - other service. `voidbot-retrieval.service` serves Qdrant and the MCP - (`compose-qdrant-1`, `compose-mcp-1`). No Postgres container, no worker, no - bot, no scheduler (`docker ps -a`). The migration map - (`notes/voidbot-daemon-organ-migration-map.md:5-14`) says it: "Discord bot, - worker, Persona scheduler, Postgres, Qdrant, and resident cognition organs: - not restored"; the Starfire scheduled tasks that used to pulse it are - deleted or disabled. `state/agent-swarm-paused.json` is `paused: true` - since 2026-07-16 with the reason "stale semantic pressure could authorize - another Persona model turn on every one-minute observation pulse. Keep - paused until pressure evidence is consumed only by a committed turn and - cannot be replayed." No commit has touched `apps/persona-scheduler` or - `persona-state-service.ts` since 2026-08-01 (`git log --since`), so that - defect is not recorded as fixed. Follow-up `voidbot-pressure-replay-pause`. +- VoidBot's `persona-scheduler` and initiative engine are **not** the waking + organ. Epiphany's resident Self already has the primitive VoidBot lacked: + a single-consumption grant consumed by one launched turn (B11), which is why + VoidBot's pause reason cannot recur here. Follow-up + `voidbot-pressure-replay-pause` is withdrawn; its rule is pinned in the + Epiphany cut instead. +- VoidBot's Interpreter prompt (`SAY`, `STATE NOTE` text blocks parsed by rules) + is prior art; Epiphany's Interpreter emits a strict typed schema + (`persona_conversation.rs`, conversation map cut line 4). +- VoidBot's registry (`repo-discord-identities.json`) is not her identity; + Bifrost's target binding (B5) and the Huginn instance are. +- VoidBot's `globalHeat` is not the dial's home (question `dial-home`). +- What is borrowed: the `gamecult.persona_state.v0` schema (Epiphany publishes + it; VoidBot's zod types and projector are the only readers today) and the + one-shot `persona:migrate-portable` as the seed path for her first document + (its target becomes Huginn's `persona_put`, run by the operator-facing Self; + spec `cut-huginn-persona-state`). -So "the agency machinery already built with voidbot" is: an initiative -engine and turn assembler that are sound and idle, a state family that is -the standard, a job queue and worker that need Postgres and a model provider -that no longer exists for this account, and a mouth that Bifrost already -provides on the host. The fork is how much of the dead body to revive -(question `persona-body-shape`). - -### B8. The burn-rate dial already exists, and nobody has turned it - -- VoidBot's swarm control: document type `voidbot.swarm_control_state`, - schema `voidbot.swarm_control_state.v1`, key `voidbot-swarm`, field - `globalHeat` read only when finite and in `0.05..2` - (`control-source.ts:28-45`); store `VOIDBOT_SWARM_CONTROL_STORE` or - `/.voidbot/private/swarm-controls.cc`. -- Semantics (`initiative-engine.ts:100-102, 568-570`): a participant's - effective speed is `speed * heat * dynamicHeat`, clamped `0.1..12`, where - `heat = clamp(globalHeat * overrides, 0.05, 20)`; "heat changes recovery - speed but does not fast-forward time" (`:126`). So the dial scales how - fast initiative recovers, i.e. wake cadence, exactly as ruling - `mind-self-burn-rate` asks. -- Writer: the swarm publisher's CultMesh command `swarm.set_heat` - (`scripts/serve-voidbot-swarm-cultmesh.cjs:161-174`, validated `0.05..2`, - writes `{schemaVersion, globalHeat, commandId, updatedAt, updatedBy}`), - issued by the Eve dashboard slider (`render-voidbot-swarm-dashboard.mjs:1410-1440`: - `control.slider`, `bind: voidbot.swarm.globalHeat`, min 0.05 max 2 step - 0.05, scale caption "low 0.05 · nominal 1.00 · hot 2.00") or by - `scripts/smoke-swarm-heat-command.mjs` over `rudp://127.0.0.1:17873`. -- Live: the publisher listens on `127.0.0.1:17873/udp` (`ss -lunp`, pid - 947038) and its private dir `/srv/voidbot/swarm/state/.voidbot/private/` is - **empty**: no `swarm-controls.cc`, so the dial has never been set on - Yggdrasil and reads as `null` → config default `1`. Anyone on the host can - send `swarm.set_heat` to localhost, including a Mind's agent running there; - the operator reaches the slider only through whatever lowers the - `voidbot.swarm` Eve surface (Odin; not probed). -- Run size: nothing in VoidBot derives a per-turn budget from heat; the - Claude Code `--max-budget-usd` flag (B2) is where run size is enforced, so - "run size from the dial" is one multiplication in the actuator. +### B8. (merged into B7 and B12) ### B9. What Huginn needs to hold `gamecult.persona_state.v0` -- The schema: `F:\Projects\Epiphany\schemas\cultnet\gamecult.persona_state.v0.schema.json`, - draft-07, `additionalProperties: false` at the root, required - `schemaVersion`, `provenance` (`sourceSystem`, `sourceDocumentId`, - `sourceUpdatedAt`, `exportedAt`, `authority: canonical|projection|import`), - `personaId`, `publicName`, `presentation` (`voiceSummary` required; - `publicHandles[]`, `homeContext`, `jurisdiction`), `activationProfile` - (six required sub-objects), `thoughtMemory` (`shortTerm`, `memories`, - `incubation`), `agencyPressure.pressures`, `candidateActions.actions`, - `affect` (needs, bonds, status reads, mood, biases, doctrine stances), - `updatedAt`; optional `values[]`, `privateNotes[]`, `voidbotProjection`. - Epiphany publishes it and does not consume it; VoidBot's zod types - (`persona-state-domain.ts`) are the only typed reader. No Rust type exists. -- The mind store: `Mind::open_checked` (`crates/huginn-mind/src/mind.rs:168-187`) - refuses a store holding any type outside the fifteen it knows - (`refuse_foreign_types`, `:280`; `kind_of_type`, `docs.rs:39`): the - thirteen leaf kinds, `huginn.mind_epoch.v1` and the commit receipt. The - wire (`wire.rs:68-115`) has four operations: `Whoami`, `Admit`, `View`, - `Query`; responses `Refused(MindRefusal)` and `Deferred`. The daemon's - `handle` dispatches on the request enum (`serve.rs`). The crate depends on - `cultcache-rs`, `cultnet-rs`, `epiphany-pipeline`, `rmp-serde`, - `schemars`, `serde`, `sha2`, `serde_json` (`Cargo.toml:17-29`); no JSON - Schema validator. -- Reaching it from TypeScript: `cultmesh-ts` has `CultMesh.createRudpClient` - (`CultLib packages/cultmesh-ts/src/index.ts:6370`) and the publisher - already uses rudp document publish with receipts (B8), but no TS client - speaks `huginn.mind_request.v1`; `eureka-state` (Rust) is the only client, - and it is an MCP server for Claude, not a library. Follow-up - `huginn-ts-client`. -- The doctrine that routes Persona-state work away from Huginn: - `F:\Projects\CLAUDE.md:79-85` ("VoidBot owns the working read, projection - and migration path") and `gamecult-ops/docs/persona-state-standard.md` - lines 29-33 and 44-55 ("Huginn … As a Persona-state steward is still - unbuilt … no agent should route Persona-state work to Huginn or wait on - it"). Both change in the cut that lands the reader - (`doctrine-persona-in-huginn`), not before. +Unchanged from revision 2 (schema shape, `Mind::open_checked` type gate at +`mind.rs:168-187, 280`, wire operations, no JSON Schema validator in the crate, +the two doctrine passages in `F:\Projects\CLAUDE.md:79-85` and +`gamecult-ops/docs/persona-state-standard.md:29-33, 44-55`). Revision 3 +changes the consumer: the live writer is Epiphany's Persona service, in Rust, +through `HuginnClient` (B1), not VoidBot over a TS client. The spec's `first` +and `adds` are revised accordingly (`cut-huginn-persona-state.r2`). ### B10. Campaign scope -- VoidBot and Bifrost are not campaign repos (B1), and the target excludes - Persona-state reading. The Huginn, Epiphany, gamecult-ops and Eureka cuts - of this map fit this campaign; the VoidBot cut (`voidbot-persona-eureka`) - needs a campaign whose `repos` include `GameCult/VoidBot`. Follow-up - `persona-cuts-need-a-campaign` (owner: Self). +`eureka-substrate` holds Huginn, Epiphany, Eureka and gamecult-ops. The +connector cut lives in CodexConnector and the mouth rebinding touches only +root files on the host (gamecult-ops), so one cut of this map has no campaign +to live in. The proposal is in the rationale ("Campaign home"); follow-up +`epiphany-cuts-need-a-campaign` supersedes `persona-cuts-need-a-campaign`. + +### B11. Epiphany's resident Self at head (`epiphany-core/src/bin/epiphany-swarm.rs`, 853 lines) + +- Commands `once`, `serve`, `status`, `initialize-and-release-brake` + (`:44-46, 497-515`); `serve` requires `--max-steps 1` (`:471-478`). No + environment variables; everything is a flag (`:512-640`), including + `--model-provider`, `--model`, `--connector-endpoint` (must be loopback), + `--provider-credential`, the stores, the Bifrost feedback anchor and + `--persona-model-allowed-data-classifications`. +- `cycle()` (`:246-398`), in order: reap worker children; check the brake and, + when released, import Bifrost Persona feedback deliveries into social + pressure; bind the repo domain and ingest "current work" pressure + (`ingest_resident_self_current_work_pressure`); reload `ResidentSelfState`; + handle a prepared launch or an active turn lease; otherwise + `issue_resident_self_grant` then `prepare_resident_self_launch`; `None` is + `Sleeping`, else launch the coordinator child and wait up to 10 s for its + claim. Sleep after the cycle: `failure_backoff_seconds` (60), + `cooldown_seconds` (60) or `idle_sleep_seconds` (15) from `ResidentSelfPolicy` + (`:121-125`). Maintenance (receipt and lifecycle retention) every 60 s. +- Types (`epiphany-core/src/resident_self.rs`): `ResidentSelfState { revision, + active_turn, last_coordinator_receipt_id, next_eligible_at_millis, + consecutive_failures, prepared_launch }` (`:505-535`); + `ResidentSelfPressure { pressure_id, kind, provenance_ref, objective, status, + consumed_by_grant_id }` (`:36-59`; a CLI `--objective` becomes kind + `operator-objective`, `epiphany-swarm.rs:572-585`); `ResidentSelfGrant` + (single consumption); `ResidentSelfPolicy` (`:269-296`, model fields and + paths); the `ResidentSelfPorts` trait (`:557-568`) implemented by + `NativePorts` (`epiphany-swarm.rs:402-516`); `coordinator_argv` + (`:602-649`) carries `--model-provider`, `--model`, `--connector-endpoint`, + `--provider-credential` to the child. +- **Self makes no model call.** Its own summary: "Self may launch one bounded + coordinator turn; it cannot directly invoke model/tools, mutate Mind/Hands, + review, release, or deploy", and its wake authority: "standard heartbeat + consumes typed operator, admitted Modeling-map direction consideration, + Persona feedback, or Imagination proposal pressure and emits one + single-consumption Self grant" (`epiphany-swarm.rs:349`, journal Aug 22). + Doctrine (`epiphany-state-model/src/prompts/epiphany_doctrine.md`, 28 lines, + no code consumer): "When no typed work obligation is active, the swarm + sleeps. Heartbeat does not ruminate, dream, mutate memory, or manufacture + work." +- The coordinator it launches is `epiphany-mvp-coordinator` (2,124 lines), + which spawns `epiphany-model-runtime run-worker` (`:1658-1700`): the + Codex-era native cognition stack, closed over the provider enum of B13. + Under `operator-mind-runs-own-self` the turn her Self takes is a Eureka run, + not this coordinator (question `self-run-actuator`); the coordinator path is + demoted, not deleted in this map. +- Nothing in `epiphany-swarm.rs`, `epiphany-persona-service.rs` or + `epiphany-openai-runtime` changed after 2026-09-05; `resident_self.rs` was + touched 2026-09-15 (`00991c1b`, storeless reads). `epiphany-heartbeat` has + no binary at head (`heartbeat_state/` is empty). + +### B12. The Self run as an actuator, and where the token lives + +- The Eureka Self is a Claude Code agentic session (skill, `eureka-state` MCP, + git, subagents), not one model request. The connector (B14) transports + requests and receipts and "cannot execute a returned model tool call" + (`standalone-codex-transport-migration.md:146`). So the Self run is not a + provider call behind the connector; it is an actuation, like the coordinator + launch today, whose one runtime is Claude Code print mode. Reading the + provider ruling that way keeps the connector's boundary intact and keeps + Epiphany's own cognition (Persona turn, any native deliberation) portable. + This is a reading of the ruling, so it is a question, not a default + (`self-run-actuator`). +- Reach: the run needs `CLAUDE_CODE_OAUTH_TOKEN`, the `eureka` forge key, the + Mind's checkouts and the `eureka-state` MCP config. Epiphany's user must not + hold the token (B2). The shape that keeps it out of her reach: a template + unit `eureka-self@.service` under a dedicated user (`eureka-self`) with a + root-0600 `EnvironmentFile`, started by the resident Self through one + sudoers line (`systemctl start eureka-self@