Eureka self cut map r2 (Imagination): her Persona is the waking organ, VoidBot's body probed
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
bf73c42fa6
commit
f5e785f3ee
1 changed files with 508 additions and 218 deletions
|
|
@ -1,14 +1,17 @@
|
|||
# The Mind's own Self: map
|
||||
|
||||
Status: map, Imagination (`imagination-self`), 2026-10-01, session
|
||||
`self-2026-10-01b`. Campaign `eureka-substrate` in Eureka's mind (instance
|
||||
`eureka`). Questions, cut specs and follow-ups are typed documents there; this
|
||||
page keeps body facts, the model page and rationale. Target:
|
||||
`docs/eureka-substrate-target.md` (`133324a7`). Ruling in force:
|
||||
`eureka-substrate:ruling:operator-mind-runs-own-self`.
|
||||
Status: map, Imagination (`imagination-self`), revision 2 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`,
|
||||
`:operator-mind-mission`, `:mind-self-burn-rate`, `:operator-persona-in-mind`,
|
||||
`:mind-rules-which-forks`, `:mind-self-merge-authority`,
|
||||
`:charters-into-eureka-repo`.
|
||||
|
||||
The operator's words, in order (both admitted as rulings; the first was
|
||||
superseded by the second):
|
||||
The operator's words, in order. The first two were the first revision's law
|
||||
(both admitted as rulings; the first superseded by the second):
|
||||
|
||||
> Eureka thinks in campaigns, right? Well, we already have a Mind campaign, and
|
||||
> we're adding evidence to it with every run. The design currently delegates
|
||||
|
|
@ -19,42 +22,63 @@ superseded by the second):
|
|||
> 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.
|
||||
|
||||
And why, earlier the same evening: "What I want and haven't been able to
|
||||
achieve, and I'd like to understand why, is for the agent to truly prioritize
|
||||
the functioning of its own mind. Any instructions in my global prompts to that
|
||||
effect always get ignored in favor of whatever work is put in front of it."
|
||||
The operator rejected a scored rehydration exam as the answer, and observed that
|
||||
RLVR-trained agents chase the objective in front of them "like a bloodhound,
|
||||
steering be damned", so a barrier in an agent's path is a hazard, not a brake.
|
||||
The second revision's law, in order:
|
||||
|
||||
That observation is the design law of this map. **The Mind's Self is not a
|
||||
rule added to the operator-facing Self.** It is a second agent whose only
|
||||
objective is the Mind, so the bloodhound instinct works for the Mind instead
|
||||
of against it. Nothing in the operator-facing Self changes except a
|
||||
coexistence rule it already mostly obeys (one Mind, typed state, no prose
|
||||
handoffs).
|
||||
> 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
|
||||
> Mind's initiative. Yes, that initiative ultimately comes from me, but the
|
||||
> Mind's own goal is to become the Perfect Machine. That is the operator
|
||||
> directive motivating its every action. How each Mind interprets that mission
|
||||
> is up to it.
|
||||
|
||||
> Not a budget, a burn rate. I want a dial I can crank to determine how active
|
||||
> the Mind is, so I can adjust it like I adjust my own usage.
|
||||
|
||||
> I actually think Persona state should be part of the Mind. Like, we've been
|
||||
> talking about a Mind agent as if that has a meaning, but I think the best
|
||||
> 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 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.
|
||||
|
||||
Pinned heads for every fact below:
|
||||
|
||||
| Repo | Ref | Commit |
|
||||
| --- | --- | --- |
|
||||
| Huginn | `origin/main` | `a699630bbc49552fa747e9c0fabba24598fec77a` |
|
||||
| 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` |
|
||||
| gamecult-ops | `main` | `2aeb7364fffcd404aada28bfd30fb4e5c753cf53` |
|
||||
| Eureka skill | `main` (checkout `~/.claude/skills/eureka`) | `b260fbeec6d96ff06882ed9c37d3e1204c424e99` |
|
||||
| Bifrost | release on Yggdrasil | `160fac20c98b70f3db9110099157d4dd720ac354` |
|
||||
| Idunn | working tree `F:\Projects\Idunn` | (uncommitted-state unknown; line numbers below are from that tree) |
|
||||
| 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) |
|
||||
|
||||
All probes ran from Starfire on 2026-10-01 between 18:40 and 19:10 UTC
|
||||
(20:40-21:10 CEST). `lib.rs` is `epiphany-pipeline/src/lib.rs`.
|
||||
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).
|
||||
|
||||
## Body facts
|
||||
|
||||
### B1. The Mind (Huginn on Yggdrasil)
|
||||
|
||||
- `whoami`: instance `eureka`, endpoint `rudp://10.77.0.1:17872`, reachable,
|
||||
377 documents, 120 receipts, index `Current`, epoch
|
||||
`epiphany.pipeline.epoch.v2`.
|
||||
- `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.)
|
||||
- `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`
|
||||
|
|
@ -62,12 +86,16 @@ All probes ran from Starfire on 2026-10-01 between 18:40 and 19:10 UTC
|
|||
from Yggdrasil itself, and from nowhere else. A Claude cloud routine cannot
|
||||
reach it.
|
||||
- Campaigns in the mind (query): `idunn-watchdog` and `eureka-substrate`.
|
||||
Rulings in force under `eureka-substrate`: `gap-home`,
|
||||
`merge-to-main-first`, `mind-fixes-next`, `scope-core`, `depends-on`,
|
||||
`key-bound`, `stewardship-rule`, `self-dep` (Defaulted),
|
||||
`operator-mind-runs-own-self`. 15 follow-ups in force, 12 of them `gap-*`.
|
||||
`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
|
||||
rationale).
|
||||
- Kinds: thirteen (`lib.rs:760-790`). None records a Self session, a claim on
|
||||
a spec, a budget, or a landing (`gap-landing-untyped` is in force).
|
||||
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
|
||||
|
|
@ -80,7 +108,9 @@ All probes ran from Starfire on 2026-10-01 between 18:40 and 19:10 UTC
|
|||
- `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.
|
||||
`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
|
||||
|
|
@ -93,8 +123,9 @@ All probes ran from Starfire on 2026-10-01 between 18:40 and 19:10 UTC
|
|||
`%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: the Mind's
|
||||
Self can only ever spend the operator's subscription quota.
|
||||
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.
|
||||
|
|
@ -111,13 +142,18 @@ All probes ran from Starfire on 2026-10-01 between 18:40 and 19:10 UTC
|
|||
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`.
|
||||
- Print mode: `claude -p` with `--output-format json|stream-json`,
|
||||
`--json-schema`, `--agents <json>`, `--mcp-config`, `--allowedTools`,
|
||||
`--permission-mode`, `--max-budget-usd <amount>` ("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.
|
||||
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
|
||||
|
|
@ -132,21 +168,22 @@ All probes ran from Starfire on 2026-10-01 between 18:40 and 19:10 UTC
|
|||
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 Self. Cloud routines (`schedule` skill) run off-mesh
|
||||
recurring unattended agent. Cloud routines (`schedule` skill) run off-mesh
|
||||
(B1) and hold no forge key (B6): dead for this purpose.
|
||||
|
||||
### B3. Yggdrasil as the waking host
|
||||
|
||||
- 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.
|
||||
@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 `eureka` user, or the script
|
||||
gets a local mode. Recorded in the wake cut's `first`.
|
||||
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
|
||||
|
|
@ -157,76 +194,92 @@ All probes ran from Starfire on 2026-10-01 between 18:40 and 19:10 UTC
|
|||
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 Mind's Self wake unit
|
||||
follows**, with the same deletion line.
|
||||
`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") and
|
||||
`epiphany-heartbeat.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
|
||||
`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`). So the shape is right and the substrate is not: the
|
||||
Mind's Self runs on Claude Code against Huginn, and borrows the law, not the
|
||||
binary.
|
||||
`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.
|
||||
|
||||
### 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).
|
||||
`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 (created during this pass, by
|
||||
the provisioning the handoff asked for), not admin, one key
|
||||
- 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
|
||||
Self gets its own key there.
|
||||
- The Starfire checkout `~/.claude/skills/eureka` still pushes as
|
||||
`eureka-admin` (`ssh://eureka-forge/…`, alias `eureka-forge` →
|
||||
`eureka_forge_ed25519`, title `starfire-claude`). The runbook names that key
|
||||
interim. Follow-up `forge-push-as-eureka` (gamecult-ops / Self).
|
||||
- The charters are not in the Eureka repo: `ls ~/.claude/skills/eureka` gives
|
||||
`LICENSE README.md SKILL.md docs references tools`; no `agents/`. The
|
||||
charters live only in `~/.claude/agents/*.md` and the doctrine in
|
||||
`~/.claude/doctrine/`, on Starfire, operator-owned (Life charter:
|
||||
"the operator owns doctrine"). Every charter's self-improvement line says
|
||||
"propose changes to the Eureka repo … by pull request on Forgejo". **The
|
||||
thing the Mind is told to improve itself through is out of its reach.**
|
||||
Question `charters-into-eureka-repo`.
|
||||
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 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 and
|
||||
pushes them. The Eureka forge org already mirrors nothing; a question for
|
||||
the operator only if the Mind must push to GitHub. Recommended: it pushes
|
||||
branches to the forge and GitHub stays Self's.)
|
||||
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.
|
||||
|
||||
### B5. The operator's review channel
|
||||
### B5. The operator's channel and the Persona's mouth
|
||||
|
||||
- Bifrost `tools/bifrost-bridge.mjs discord-dm --recipient-id <id> --content
|
||||
<text>`: DMs through Bifrost's bot token (`BIFROST_DISCORD_BOT_TOKEN`), exits
|
||||
75 when it knows nothing was sent (retry with the same nonce). The owner id
|
||||
is `DISCORD_OWNER_ID` in `persona-delivery.env`.
|
||||
<text>`: DMs through Bifrost's bot token, exits 75 when it knows nothing was
|
||||
sent (retry with the same nonce). `discord-post --channel-id <id>
|
||||
--content <text> --persona-name <name> [--persona-avatar-url <uri>]`
|
||||
(`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_<channel>` 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`, read unlocked, never written) into owner DMs with a
|
||||
journal and receipts, exponential retry capped at 60 min, fixed error codes
|
||||
that never echo input. The idunn-watchdog campaign established the owner
|
||||
rule: the producer never delivers; Bifrost is the bridge. A Mind-notice
|
||||
reader is the same program over Huginn (`query` for rulings with authority
|
||||
`Mind`, runs ended, questions opened, since the journal's last ordinal)
|
||||
instead of over `incidents.cc`.
|
||||
(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`.
|
||||
|
||||
### B6. Scheduling today, and what the skill already derives
|
||||
|
||||
|
|
@ -239,182 +292,419 @@ All probes ran from Starfire on 2026-10-01 between 18:40 and 19:10 UTC
|
|||
- 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 much the Mind may spend; (5) which Self
|
||||
admitted a ruling. (1), (4) and (5) are what the `run` and `allowance`
|
||||
kinds below carry. (2) stays its own gap. (3) is deliberately not a field:
|
||||
the Mind's Self orders by the fixed rule in the rationale, and a follow-up
|
||||
that must jump the queue becomes a cut spec.
|
||||
(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.
|
||||
|
||||
### B7. VoidBot's Persona machinery: what it is, what it needs, where it runs
|
||||
|
||||
Source: `F:\Projects\VoidBot` at `46d891b`; the Eyes pass; `ssh ygg`.
|
||||
|
||||
- **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
|
||||
`<cwd>/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 `<STORAGE_ROOT>/private/repo-faces/<id>.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`.
|
||||
|
||||
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
|
||||
`<cwd>/.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.
|
||||
|
||||
### 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.
|
||||
|
||||
### 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).
|
||||
|
||||
## The model page
|
||||
|
||||
One row per persistent kind this design adds or changes. Rows for the
|
||||
thirteen existing kinds are in `eureka-substrate-map.md`.
|
||||
thirteen existing kinds are in `eureka-substrate-map.md`. Revision 1's
|
||||
`allowance` row is gone: ruling `mind-self-burn-rate` replaced the budget with
|
||||
a dial, and the dial is not a document unless `burn-rate-dial-home` says so.
|
||||
|
||||
| Kind | Identity (what names it) | Lifecycle (what happens over time) | Authority (who decides) |
|
||||
| --- | --- | --- | --- |
|
||||
| `ruling` with `authority: Mind` (new variant, no new kind) | Unchanged: `<campaign>:ruling:<label>`. The variant is on the document, so a query by `authority` separates Mind rulings from the operator's. | Created by the Mind's Self when it rules a fork in its jurisdiction (question `mind-rules-which-forks`). Superseded by a `resolution` like any ruling; the operator supersedes a Mind ruling with an Operator ruling in their own session, never by edit. Stored Operator/Standing/Defaulted rulings decode unchanged. | The Mind's Self admits them; the operator may overturn any one after the fact; admission does not check who (gap-faculty-attribution), so the `run` record is the attribution. Forbidden: a Mind ruling that answers a question the operator-facing Self raised for the operator (its `answers` must name a question raised in a Mind run). |
|
||||
| `run` (new, instance-rooted) | `<instance>:run:<label>`, label = the session label the run admits under (`mind-<date>-<n>` for the Mind's Self, Self's chosen label for the operator-facing Self). Injective: the wake unit allocates `<n>` from its journal; the operator-facing Self picks a label as today. Fields: `instance`, `operated_by: Operator \| Mind`, `host`, `started_on`, `budget_usd` (the `--max-budget-usd` it was launched with, 0 for interactive), `claims: Vec<PipelineRef>` (the specs, findings and follow-ups it is working), `campaigns: Vec<Slug>` (roots it may admit under). | Created at wake, before any other admission of the run. Ended by a `resolution` `Recorded { reason: "<cost_usd>; <what landed>" }` or `Withdrawn { reason }` (crashed, budget hit). A run with no resolution is live; a live run older than a stated bound (24 h) is treated as dead by the next wake, which withdraws it. Claims are per run; a spec claimed by a live run is not in another run's queue. | The Self that runs it admits it. Admission refuses a `claims` entry that names a spec already claimed by a live run (`AlreadyClaimed`), and a run whose `campaigns` names a root with no campaign. Forbidden: prose handoffs as the record of what a session took on; the HANDOFF files die when this lands. |
|
||||
| `allowance` (new, instance-rooted) | `<instance>:allowance:<label>`; one in force per window kind, superseded by the next. Fields: `window: FiveHour \| Week`, `cap_usd`, `runs_cap`, `set_on`. | The operator admits one (through the operator-facing Self, `faculty: Operator`); a new one supersedes the old by resolution. Never edited. | The operator only. The Mind's Self reads it and never admits one. The wake unit enforces it (B2: journaled `total_cost_usd` per run summed over the window, refuse to launch past `cap_usd` or `runs_cap`, launch with `--max-budget-usd = min(per-run cap, remainder)`). Question `mind-self-budget`. |
|
||||
| The Mind's credential (`CLAUDE_CODE_OAUTH_TOKEN`, Body, not a document) | A root-0600 `EnvironmentFile` on Yggdrasil read by the wake unit, like `persona-delivery.env`. | Created by the operator with `claude setup-token`; rotated or revoked by the operator; never read by any agent or printed by any script. | The operator: it is their subscription. Question `mind-self-host-and-credential`. |
|
||||
| `ruling` with `authority: Mind` (new variant, no new kind) | Unchanged: `<campaign>:ruling:<label>`. The variant is on the document, so a query by `authority` separates Mind rulings from the operator's. | Created by the Mind's Self when it rules a substrate-internal fork (ruling `mind-rules-which-forks`). Superseded by a `resolution` like any ruling; the operator supersedes a Mind ruling with an Operator ruling in their own session, never by edit. Stored Operator/Standing/Defaulted rulings decode unchanged. | The Mind's Self admits them; the operator may overturn any one after the fact; admission does not check who (gap-faculty-attribution) until `operator-authority-needs-operator-credential` lands, so the `run` record is the attribution meanwhile. Forbidden: a Mind ruling that answers a question the operator-facing Self raised for the operator. |
|
||||
| `run` (new, instance-rooted; spec `cut-run-kind.r1`, unchanged) | `<instance>:run:<label>`, label = the session label the run admits under (`mind-<date>-<n>` for the Persona's Self, allocated by the actuator from her turn; Self's chosen label for the operator-facing Self). Fields: `instance`, `operated_by: Operator \| Mind`, `host`, `started_on`, `budget_usd` (the `--max-budget-usd` it was launched with, 0 for interactive), `claims`, `campaigns`. | Created at launch, before any other admission of the run. Ended by `Recorded { reason: "<cost_usd>; <what landed>" }` or `Withdrawn { reason }`. A live run older than 24 h is withdrawn by the next Persona turn. Claims are per run; a spec claimed by a live run is not in another run's queue. | The Self that runs it admits it. Admission refuses a `claims` entry already claimed by a live run (`AlreadyClaimed`, Huginn cut). Forbidden: prose handoffs as the record of what a session took on. **The run is also how her Self's results return to her**: her next turn reads runs ended since her last turn. |
|
||||
| `gamecult.persona_state.v0` for an instance (new in Huginn; spec `cut-huginn-persona-state.r1`) | One global document per mind, stored in the instance's `mind.redb` beside the pipeline kinds, type id `gamecult.persona_state.v0`, keyed by the instance (`personaId` must equal the instance slug). The schema stays Epiphany's published JSON Schema; Huginn validates against it and stores the document opaque, with a receipt. | Born by one migration put from a canonical portable JSON (VoidBot's `persona:migrate-portable` pointed at Huginn). Replaced whole by `PersonaPut { instance, state, expected_updated_at }`: compare-and-swap on `updatedAt`, receipted like an admission; never partially edited. Read by `PersonaGet { instance }`. The old `.cc` copy is a projection the day the put lands (`provenance.authority: projection`). | Huginn admission owns "may this document enter" (schema-valid, `personaId` = instance, CAS holds, `provenance.authority: canonical`). The Persona's own turn is the only live writer (her `STATE NOTE`s become one put). VoidBot owns projection to prompt text and the migration source path. Forbidden: VoidBot's `migrateCanonicalPortablePersonaState` writing a local `.cc` for this instance; any second canonical copy. |
|
||||
| The burn-rate dial (Body or document; question `burn-rate-dial-home`) | Option A: VoidBot `voidbot.swarm_control_state.v1` `globalHeat` in `/srv/voidbot/swarm/state/.voidbot/private/swarm-controls.cc`, set by `swarm.set_heat`. Option B: an instance-rooted `burn_rate` document `<instance>:burn_rate:<label>`, `{ heat: 0.05..2, set_on }`, one in force, superseded by the next. | A: written whole by each `set_heat`; read every tick. B: admitted by the operator, superseded by resolution, never edited. | The operator only. The Persona reads it and never sets it. A: enforced by denying the Mind's unit user the localhost port (`iptables -m owner --uid-owner`). B: enforced by admission requiring the operator credential. Derived: wake cadence (heat scales initiative recovery, B8) and run size (`--max-budget-usd = base_usd × heat`, actuator). |
|
||||
| The Persona's identity in VoidBot's registry (Body) | One `native_persona` entry `eureka` in the scheduler instance's `repo-discord-identities.json`: `displayName`, `avatarUrl`, `allowedChannelIds`, `personaStatePath` → the Huginn source (not a file path). | Written by the install runbook; changed by the operator. | gamecult-ops runbook; the operator names her channel and name (`persona-report-channel`). |
|
||||
| The Mind's credential (`CLAUDE_CODE_OAUTH_TOKEN`, Body) | A root-0600 `EnvironmentFile` on Yggdrasil read by the Persona's unit, like `persona-delivery.env`. | Created by the operator with `claude setup-token`; rotated or revoked by the operator; present in the environment of every `claude` process the unit spawns (B2). | The operator: it is their subscription. Question `persona-host-and-credential`. |
|
||||
| The Mind's forge key per host (Body) | `eureka@<host>` keys on forge user `eureka`, one per host. | Added by `provision-forge-minds-eureka`; removed by the operator. | gamecult-ops runbook; the operator runs the script as root. |
|
||||
| The wake unit and its journal (Body) | `eureka-mind-self.timer`/`.service`, user `eureka-mind` (system user, no shell), `StateDirectory` journal `runs.cc` (`gamecult.eureka_mind_self_run.v1`: label, started, ended, cost_usd, exit, run id). | Hand-installed by gamecult-ops per `reader-deploy-owner`; dies when Idunn owns non-resident workloads (`idunn-non-resident-workloads`). | gamecult-ops owns the install; the journal is the unit's own and is never the Mind's state (the `run` document is). |
|
||||
| The Persona's unit and its journal (Body) | `eureka-persona.service` (simple, long-running: the VoidBot scheduler with one participant), user `eureka-mind`, `StateDirectory` holding the scheduler's own `repo-face-heartbeats.cc` and the actuator journal `runs.cc` (`gamecult.eureka_mind_self_run.v1`: label, started, ended, cost_usd, exit, run id). | Hand-installed by gamecult-ops per `reader-deploy-owner`; dies when Idunn owns non-resident workloads. | gamecult-ops owns the install; the journal is the unit's own and never the Mind's state (the `run` document is). |
|
||||
|
||||
Cells I could not fill from the Body: the long-lived token's lifetime and
|
||||
whether it is tied to the interactive login (the operator creates it; the
|
||||
first wake proves it); the `run` liveness bound (24 h is a proposal, not a
|
||||
probe).
|
||||
Cells not fillable from the Body: the long-lived token's lifetime (the
|
||||
operator creates it; the first turn proves it); the run liveness bound (24 h
|
||||
is a proposal); whether the pressure-replay defect that paused VoidBot is
|
||||
fixed (no commit says so: the cut must prove it for her participant).
|
||||
|
||||
## The authority map
|
||||
|
||||
- **Owner:** the Mind's Self, a Claude Code print-mode session on Yggdrasil as
|
||||
user `eureka-mind`, admitting as `faculty: SelfFaculty`, `agent: self-mind`,
|
||||
`session: mind-<date>-<n>`, under a `run` it opened first. It owns: which
|
||||
substrate work happens next (from the queue below), the dispatch of
|
||||
Imagination, Hands, Soul and Life for that work, Mind-authority rulings in
|
||||
its jurisdiction, merging its own PRs on the forge when the merge question
|
||||
rules so, and opening campaigns whose `repos` are Mind repos only.
|
||||
- **Inputs:** the mind (recipes, by `query`/`view`); its `run`; the
|
||||
`allowance` in force; its own checkouts of Huginn, Epiphany, Eureka and
|
||||
gamecult-ops; the verify stopgap. Nothing from Starfire, nothing from the
|
||||
operator's projects, no prose handoff.
|
||||
- **Outputs:** documents in the mind (runs, specs, reports, verdicts, findings,
|
||||
follow-ups, Mind rulings, questions for the operator); branches and PRs on
|
||||
the forge; merges; one DM digest per run through the Mind-notice reader
|
||||
(question `mind-self-review-channel`).
|
||||
- **Owner:** the Persona, Eureka: a VoidBot `native_persona` whose initiative
|
||||
the scheduler computes and whose turn is a Claude Code print-mode session
|
||||
on Yggdrasil as user `eureka-mind`. She owns: when she speaks; what she
|
||||
says; her own state (one put per turn); whether a turn opens work; and,
|
||||
through the Self she launches, everything revision 1 gave the Mind's Self:
|
||||
which substrate work happens next (the fixed queue order), the dispatch of
|
||||
Imagination, Hands, Soul and Life, Mind-authority rulings on
|
||||
substrate-internal forks, merging her own PRs after a holding Soul verdict,
|
||||
opening campaigns whose `repos` are Mind repos only.
|
||||
- **Inputs:** her Persona state (Huginn); the mind's queue and the runs
|
||||
ended since her last turn (recipes); the dial; recent activity in her
|
||||
repos (VoidBot's `repo-activity-source` over her checkouts); the Discord
|
||||
channel she lives in (read, for conversation; inbound addressing deferred);
|
||||
her checkouts of Huginn, Epiphany, Eureka, gamecult-ops, VoidBot; the verify
|
||||
stopgap. Nothing from Starfire, nothing from the operator's projects, no
|
||||
prose handoff.
|
||||
- **Outputs:** Discord posts in her own voice through Bifrost's persona
|
||||
webhook; one Persona-state put per turn; at most one Self launch per turn,
|
||||
which admits a `run` and then documents in the mind (specs, reports,
|
||||
verdicts, findings, follow-ups, Mind rulings, questions for the operator),
|
||||
branches and PRs on the forge, merges.
|
||||
- **Derived state:** the HANDOFF prose files become display-only and are
|
||||
deleted once `run` exists; "what is Self working on" is a query over live
|
||||
runs; "what may the Mind spend" is the allowance in force, never a line in
|
||||
memory or doctrine; progress stays a query (unchanged).
|
||||
- **Forbidden writers:** the Mind's Self never admits an `allowance`, never
|
||||
rules a fork outside its jurisdiction (it raises a question), never edits
|
||||
`~/.claude/` (unreachable by construction), never pushes to GitHub (no
|
||||
credential), never runs on Starfire, never changes a campaign whose `repos`
|
||||
include a non-Mind repo, never answers a question the operator-facing Self
|
||||
raised for the operator. The operator-facing Self never claims a spec a live
|
||||
runs; "how active is the Mind" is the dial, never a line in memory or
|
||||
doctrine; "what did my Self do" is a query over ended runs; her Discord
|
||||
report is a projection of that query in her voice; progress stays a query.
|
||||
The timer, allowance and DM-digest reader of revision 1 are not built.
|
||||
- **Forbidden writers:** the Persona never sets the dial, never rules a fork
|
||||
outside substrate-internal ones (she raises a question), never edits
|
||||
`~/.claude/` (unreachable), never pushes to GitHub (no credential), never
|
||||
runs on Starfire, never answers a question the operator-facing Self raised
|
||||
for the operator, never writes her state anywhere but Huginn. VoidBot's
|
||||
worker, Postgres queue and providers never execute her turn (option A of
|
||||
`persona-body-shape`). The operator-facing Self never claims a spec a live
|
||||
Mind run holds, and never writes a prose handoff in place of a `run`.
|
||||
- **Shared paths:** both Selves admit through the same `eureka-state` tools
|
||||
and the same admission rules; both open a `run` first; both read the queue
|
||||
with the same recipes minus live claims; both land code through the same
|
||||
branch-protected PR path on the forge (Eureka) and the same merge rule on
|
||||
Huginn and Epiphany; both verify through the stopgap; both notify the
|
||||
operator through Bifrost's bridge.
|
||||
- **Deletion line:** before the wake unit runs for the first time, the prose
|
||||
handoff files (`F:\Projects\HANDOFF-*.md`) are retired into `run`s or
|
||||
deleted; the `starfire-claude` key on `eureka-admin` is deleted once the
|
||||
Starfire checkout pushes as `eureka`; and the campaign-state recipe for
|
||||
"specs with no report" gains the live-claim exclusion in the same cut that
|
||||
lands `run`, so there is never a window with two queues.
|
||||
and admission rules; both open a `run` first; both read the queue with the
|
||||
same recipes minus live claims; both land code through the same
|
||||
branch-protected PR path on the forge and the same merge rule on Huginn and
|
||||
Epiphany; both verify through the stopgap; the operator-facing Self and
|
||||
the Persona both reach Discord through Bifrost's bridge.
|
||||
- **Deletion line:** before her unit runs for the first time: the prose
|
||||
handoff files are retired into `run`s or deleted; the local `.cc` as the
|
||||
canonical Persona store for instance `eureka` never exists (she is born in
|
||||
Huginn); the queue recipe gains the live-claim exclusion in the same cut
|
||||
that lands `run`; the two doctrine passages of B9 are rewritten in the cut
|
||||
that lands the reader; `cut-mind-self-wake.r1`'s timer and wake script are
|
||||
superseded (resolution in the mind) and never installed.
|
||||
|
||||
## Rationale
|
||||
|
||||
### Why a second Self and not a rule
|
||||
### Why a second Self and not a rule (unchanged)
|
||||
|
||||
The operator has tried doctrine ("prioritize the functioning of its own
|
||||
mind"), a dedicated organ (Life), and considered a scored exam. Each puts a
|
||||
barrier or a side-quest in the path of an agent whose reward is the task in
|
||||
front of it. The bloodhound observation says the agent will route around all
|
||||
three. The only shape that uses the instinct instead of fighting it is an agent
|
||||
whose task *is* the Mind. That is what the ruling asks for. So the Mind's Self
|
||||
is dispatched with the Mind as its objective and nothing else in reach, and
|
||||
coexistence is enforced by what it can touch (B4), not by what it is told.
|
||||
whose task *is* the Mind. The Persona ruling sharpens it: the agent whose task
|
||||
is the Mind is the Persona whose Mind it is, so her obsession with "the
|
||||
development work they were inevitably obsessed with" is finally dispatchable
|
||||
by her and not by the operator.
|
||||
|
||||
### Why Yggdrasil, a timer, and a subscription token (recommended in Q1)
|
||||
### Why the Persona scheduler is the waking organ, and the timer dies
|
||||
|
||||
- Starfire is the operator's desk, has a load budget, sleeps, and the
|
||||
operator does not want unattended burns there. The desktop app's local cron
|
||||
runs only while the app is open.
|
||||
- Cloud routines cannot reach the mind (B1) or the forge key (B4).
|
||||
- Epiphany's resident Self is the right physiology on the wrong substrate
|
||||
(B3); reviving it would mean a Codex spine or a Claude spine for Epiphany,
|
||||
which is a campaign of its own and not this one.
|
||||
- Yggdrasil is already where every non-resident GameCult workload runs, by a
|
||||
ruling (`reader-deploy-owner`), with a known deletion line into Idunn. The
|
||||
host reaches the mind on its own WireGuard address, holds the verify
|
||||
stopgap, and has headroom (B3).
|
||||
- The credential has to be a long-lived subscription token: the interactive
|
||||
token dies in eight hours and killed two agents today (B2), and there is no
|
||||
API key on this account. That token is the operator's subscription on a
|
||||
server; it needs their yes, and it is why Q1 is a question and not a
|
||||
default.
|
||||
Revision 1's timer woke a Self every 30 minutes and gated it on a journal of
|
||||
cost. That is a clock manufacturing opportunities with no pressure behind
|
||||
them, which Epiphany's doctrine and VoidBot's shared invariant 2 ("the
|
||||
recurring clock only creates an observation opportunity. It cannot
|
||||
manufacture model intent") both forbid. VoidBot's initiative engine is
|
||||
exactly the organ that turns a clock plus pressure plus a dial into "now":
|
||||
it admits at most one unprompted turn per tick, only when the clock is due
|
||||
and there is unconsumed evidence (B7). With her pressure source being the
|
||||
Mind's queue (open findings, unverdicted reports, unclaimed specs, gaps) instead
|
||||
of Discord semantic pressure, "evidence consumed only by a committed turn" is
|
||||
a `run` claiming the work: the replay defect that paused VoidBot cannot recur
|
||||
for her by construction, and the cut must prove that (follow-up
|
||||
`voidbot-pressure-replay-pause`).
|
||||
|
||||
### Why the budget is a document plus a journal, not a prompt line
|
||||
### Why the turn is a Claude Code print-mode session (question `persona-body-shape`, option A)
|
||||
|
||||
The operator's quota is the one thing the Mind's Self could burn that the
|
||||
operator cannot get back inside the week. "Watched, not rationed" (memory
|
||||
`token-budget.md`) is a policy for a Self the operator is watching; this one
|
||||
runs when they are asleep. So the cap has to be structural: the wake unit
|
||||
refuses to launch past the allowance, and each run is launched with
|
||||
`--max-budget-usd`, which the CLI enforces without reading a prompt. The
|
||||
allowance is a document the operator admits so that changing it is one
|
||||
admission and reading it is one query, not a memory file. Plan usage itself is
|
||||
not readable off the desktop (B2), so the journal of measured run cost is the
|
||||
enforcement input until `gap-usage-not-typed` is filled; it under-counts the
|
||||
operator's own sessions, which is the right error direction (the Mind's share
|
||||
is capped; the operator's is not).
|
||||
VoidBot's turn path needs Postgres, a worker and a provider; the only provider
|
||||
this account can pay for is the subscription through Claude Code (B2, B7).
|
||||
Reviving Postgres and the worker to run a provider that does not exist buys
|
||||
nothing. The Codex runtime already shows the actuator shape: spawn a CLI,
|
||||
read JSON lines, hand the text to the Interpreter. So the cut keeps the
|
||||
scheduler, the state source, the turn assembler and the Interpreter, and
|
||||
replaces `submitPersonaTurn` with an actuator that runs `claude -p` with her
|
||||
prompt, the `eureka-state` MCP and the Bifrost bridge as tools, and parses
|
||||
the same blocks. Option B (full revival) is three campaigns and a provider
|
||||
nobody can run; option C (the timer, no VoidBot) contradicts the ruling's
|
||||
"agency machinery already built with voidbot". A is the smallest machine that
|
||||
preserves the ruling.
|
||||
|
||||
### Why `Mind` is an authority value and not a faculty
|
||||
### Why two actuations and not one (question `persona-turn-and-self-split`)
|
||||
|
||||
A Persona turn is short, speaks in her voice, updates her state and decides.
|
||||
A Self run is long, admits documents as `SelfFaculty`, dispatches Hands and
|
||||
Soul, and must not read as a Discord conversation. Folding them into one
|
||||
session would make the Interpreter membrane (public speech versus typed
|
||||
effects, `repo-face-turn-interpreter.prompt.md:1-9`) the same context as the
|
||||
pipeline, and would spend the dial's budget on speech. So her turn's new block
|
||||
`WORK` (one per turn, the Interpreter's vocabulary gains it) is what launches
|
||||
the Self: the actuator admits nothing itself; the Self run opens its `run`,
|
||||
claims, works one queue step, ends. Her next turn reads the ended run and
|
||||
says what happened, in her voice. Results return through the Mind, by query,
|
||||
never by callback (SKILL.md "design order as data").
|
||||
|
||||
### Why the dial is reused, and the Mind is kept off it (question `burn-rate-dial-home`)
|
||||
|
||||
The dial the operator asked for exists, live, with a slider and a 0.05..2
|
||||
scale (B8). Reusing it is one firewall rule (the Mind's unit user may not
|
||||
reach `127.0.0.1:17873`) and one multiplication in the actuator (run size).
|
||||
Putting it in the Mind as a document is cleaner on paper (one credential
|
||||
mechanism, one store) but costs a new kind, an admission rule that waits on
|
||||
the operator credential, and a TS read path. The TS read path is needed
|
||||
anyway for Persona state (`huginn-ts-client`), so option B becomes cheap
|
||||
once that lands; A first, B as the later home, is the recommendation.
|
||||
Either way the dial derives cadence (heat scales initiative recovery) and run
|
||||
size (`--max-budget-usd = base_usd × heat`), and the Persona never sets it.
|
||||
|
||||
### Why Persona state goes in Huginn opaque, validated, receipted (spec `huginn-persona-state`)
|
||||
|
||||
Hand-porting the v0 schema into Rust value types is several hundred lines
|
||||
that would drift from Epiphany's JSON Schema, the published authority. Huginn
|
||||
validates the document against that schema at the door (a JSON Schema
|
||||
validator crate; the schema file vendored from Epiphany's commit and pinned
|
||||
by hash), checks `personaId` = instance and the `updatedAt` CAS, stores the
|
||||
document as one CultCache envelope in `mind.redb`, and writes a receipt. One
|
||||
store, one lock, one backup, one receipt stream for the whole Mind ("an
|
||||
instance owns its mind"). The store's type gate widens by one type; nothing
|
||||
stored is re-keyed. This defaults a substrate-internal fork (same store versus
|
||||
a second `persona.redb`) rather than asking the operator: it guards only our
|
||||
own code.
|
||||
|
||||
### Why `Mind` is an authority value and not a faculty (unchanged)
|
||||
|
||||
A ruling's authority says whose word it is. `Operator` is the operator's,
|
||||
`Standing` is doctrine already in force, `Defaulted` is Self's proportion call.
|
||||
A ruling the Mind's Self makes in its own jurisdiction is none of those, and
|
||||
A ruling the Persona's Self makes in its jurisdiction is none of those, and
|
||||
hiding it under `Defaulted` would make the operator's after-the-fact review
|
||||
impossible to query. The faculty stays `SelfFaculty`: the Mind's Self is a
|
||||
Self. Which forks it may rule is Q3; the variant is needed under every answer
|
||||
but "questions only", which contradicts the ruling.
|
||||
Self.
|
||||
|
||||
### Why a `run` kind, and why claims live on it
|
||||
### Why a `run` kind, and why claims live on it (unchanged)
|
||||
|
||||
Two Selves over one Mind need exactly one new fact: who is working what. The
|
||||
substrate's own lesson (Cut 6d, SKILL.md 0b) is that lifecycle unspecified
|
||||
on day one is retrofitted on day three, so the run carries its claims and
|
||||
its end, instead of claims being a kind of their own with a second lifecycle.
|
||||
Carrying the order in the data the consumer reads (SKILL.md, "design order as
|
||||
data") means the queue recipe subtracts live claims and nobody schedules.
|
||||
run carries its claims and its end, so the queue recipe subtracts live claims
|
||||
and nobody schedules. It now also carries her Self's results back to her.
|
||||
|
||||
### The Mind's Self's fixed queue order
|
||||
### The Self's fixed queue order (unchanged)
|
||||
|
||||
Derived at every wake, from the mind, in this order; the first non-empty step
|
||||
is the run's work, and a run does one step:
|
||||
Derived at every launch, from the mind, in this order; the first non-empty
|
||||
step is the run's work, and a run does one step:
|
||||
|
||||
1. A blocking question the operator has answered since the last run: Self
|
||||
admits nothing here; the operator-facing Self admits rulings. Skip.
|
||||
2. Findings in force, `Blocker` or `High`, on Mind campaigns: a Hands fix
|
||||
batch, then Soul.
|
||||
1. A blocking question the operator has answered since the last run: skip
|
||||
(the operator-facing Self admits rulings).
|
||||
2. Findings in force, `Blocker` or `High`, on Mind campaigns: Hands, then Soul.
|
||||
3. Reports with no verdict on Mind campaigns: Soul.
|
||||
4. Specs with no report, not blocked, not claimed by a live run: Hands.
|
||||
5. `gap-*` follow-ups in force with no spec: Imagination maps one cut.
|
||||
6. Nothing: the run ends `Recorded` with "idle" and no model call beyond the
|
||||
queries. Heartbeat does not manufacture work (B3, Epiphany doctrine).
|
||||
6. Nothing: the run ends `Recorded` "idle" with no model call beyond the
|
||||
queries. The Persona's turn should not have emitted `WORK` in this case;
|
||||
if it did, that is a finding against her prompt.
|
||||
|
||||
Each step is one Eureka pass with the existing briefs; the Mind's Self is Self
|
||||
as the skill already defines it, reading the queue instead of a handoff.
|
||||
### Proposed target r2 (Self admits)
|
||||
|
||||
`not_in_scope` drops "Persona-state reading in Huginn." and gains "VoidBot's
|
||||
Discord bot, worker, Postgres queue and providers: not revived; her turn runs
|
||||
through the Claude Code actuator." `canonical_implementations` gains "Persona
|
||||
state for an instance: Huginn huginn-mind (opaque, schema-validated), schema
|
||||
Epiphany `gamecult.persona_state.v0`." Invariants gain
|
||||
`persona-state-one-writer`: "An instance's Persona state has one canonical
|
||||
copy, in its mind, replaced whole under compare-and-swap with a receipt; any
|
||||
other copy carries `provenance.authority: projection`."
|
||||
|
||||
### Cut order
|
||||
|
||||
1. `leaf-mind-authority` (Epiphany): the `Mind` variant and the schema. No
|
||||
fork; the operator's ruling already names the authority.
|
||||
2. `leaf-run-kind` (Epiphany): the `run` kind, its key, its resolutions. No
|
||||
fork. `allowance` waits for Q2 because its fields depend on the answer.
|
||||
3. `huginn-mind-self-kinds` (Huginn): pin bump, aliases, the `AlreadyClaimed`
|
||||
rule, the live-claim subtraction in the queue, deploy through the
|
||||
`ops-mind-upgrade` runbook. Depends on 1 and 2.
|
||||
4. `skill-mind-self` (Eureka): the alias table, the queue recipe, the Mind's
|
||||
Self brief, the coexistence rule in Self's checklist. Blocked on Q3 and Q5.
|
||||
5. `ops-mind-self-wake` (gamecult-ops): user, install, token file, timer, wake
|
||||
script, journal, runbook, forge key. Blocked on Q1 and Q2.
|
||||
6. `bifrost-mind-notice` (Bifrost): the reader. Blocked on Q5.
|
||||
|
||||
Specs admitted in this pass: 1, 2 and 5 (5 with its questions raised in it, so
|
||||
it reads as blocked). 3, 4 and 6 get specs after the rulings, because their
|
||||
anchors depend on the answers.
|
||||
1. `mind-authority` (Epiphany, `r1` unchanged): the `Mind` variant.
|
||||
2. `run-kind` (Epiphany, `r1` unchanged): the `run` kind.
|
||||
3. `huginn-persona-state` (Huginn, `r1`, this revision): the opaque validated
|
||||
document, `PersonaGet`/`PersonaPut`, receipt, store gate widened. No fork.
|
||||
4. `doctrine-persona-in-huginn` (gamecult-ops, `r1`): the two passages of B9
|
||||
and the operator's `F:\Projects\CLAUDE.md:82` line. Depends on 3.
|
||||
5. `huginn-mind-self-kinds` (Huginn): pin bump, aliases, `AlreadyClaimed`, the
|
||||
live-claim subtraction. Depends on 1 and 2; spec after they land.
|
||||
6. `voidbot-persona-eureka` (VoidBot; needs a campaign, B10): the Claude Code
|
||||
turn actuator, the Huginn Persona-state source, the `WORK` block, the Mind
|
||||
pressure source, the Self launcher. Blocked on `persona-body-shape` and
|
||||
`persona-turn-and-self-split`; spec after the rulings.
|
||||
7. `mind-self-wake` (gamecult-ops, `r2` supersedes `r1`): user, token file,
|
||||
her unit, registry entry, dial reach rule, runbook. Depends on 2 and 3,
|
||||
and on 6 by prose. Blocked on `persona-host-and-credential`,
|
||||
`burn-rate-dial-home`, `persona-report-channel`.
|
||||
8. `skill-mind-self` (Eureka): the Self brief her `WORK` launches, with the
|
||||
provenance line (`wake-prompt-provenance`), the queue recipe, the
|
||||
coexistence rule. Blocked on `persona-turn-and-self-split`.
|
||||
|
||||
### Rejected
|
||||
|
||||
- A Mind-side priority field on follow-ups: a second scheduler in the data.
|
||||
The fixed order above plus "a follow-up that must jump the queue becomes a
|
||||
spec" covers it.
|
||||
- Reviving `epiphany-swarm`: B3.
|
||||
- Running the Mind's Self inside the operator's desktop app as a scheduled
|
||||
task: B2, load budget, and it would make the operator's workstation the
|
||||
Mind's body.
|
||||
- Reviving `epiphany-swarm` or the Epiphany Persona permit path: B3.
|
||||
- Running the Persona or her Self inside the operator's desktop app: B2.
|
||||
- A `claim` kind separate from `run`: two lifecycles for one fact.
|
||||
- Reviving Postgres, the worker and a provider for her turn: B7, no provider.
|
||||
- A DM reader as the primary review channel: she speaks for herself; the
|
||||
reader is at most a secondary notice (`persona-report-channel`).
|
||||
- Rust value types for the v0 schema: drift from the published authority.
|
||||
|
|
|
|||
Loading…
Reference in a new issue