A tell is the message (first line carries the machine, later lines messages and organ notices); upsert before delete in the source index; the CPU reserve's own embed bounds; the tempo fallback controller merged but not wired; Mind web builds and the recorded shared origin; skill pin b22762a. The design doc's section 3 no longer says every line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
118 KiB
eureka-body: map (Imagination, 2026-10-08)
Status: map, Imagination (imagination-eureka-body), session self-2026-10-08.
Campaign eureka-body in Eureka's mind (instance eureka). Target in force
eureka-body:target:r1, whose doc is Huginn docs/eureka-self-cut.md at
570f369 (map r4 of 2026-10-01). This page keeps body facts, the model page
(step 0b) and rationale. Questions, cut specs, follow-ups and rulings are typed
documents in the mind; read them by id. Self commits this page where the
target doc lives (Huginn docs/), probably as docs/eureka-body-map.md.
Revised 2026-10-09 (Modeling, session self-2026-10-09-morning) after
leaf-into-huginn (Huginn afc35f9) and the Epiphany body teardown
(gamecult-ops 9c12adb; leftovers in follow-up
eureka-body:follow_up:epiphany-body-leftovers). Pinned heads and facts F1-F14 are
as read on 2026-10-08 and keep their tense; each carries a note where a cut has since
changed it. F15-F17 are new.
Revised again on 2026-10-09 by Imagination (imagination-brake-stop, session
self-2026-10-09-eureka) at Huginn df7507b, after leaf-into-huginn, mind-authority,
mind-control-store and run-kind had merged. New facts are F18-F20, which name the
owner of the brake stop. The Brake, Units and run rows changed, and a rationale
section was added.
Revised again on 2026-10-09 by Imagination (imagination-launch-r3, session
self-2026-10-09-eureka) at the mind-launch head 8dee875, after Soul's verdict
eureka-body:verdict:cut-mind-launch.s1 falsified that cut with nine findings. New fact
F21. The run, Repetition breaker, Brake and Units rows changed, and the rationale gained
"Who decides one live run, and who ends a dead one" and "What the breaker counts".
Specs: cut-run-live-admission.r1, cut-mind-launch.r3, cut-mind-wake.r3,
cut-brake-stop.r2.
Revised again on 2026-10-10 by Imagination (imagination-mind-units, session
self-2026-10-10-eureka) at Huginn 0c6f6cb and gamecult-ops 6b4a874, for cut
mind-units-install. New fact F22. New section "The mind units: identity, lifecycle and
authority", written before the cut was mapped. Specs: cut-mind-unit-gate.r1 (Huginn) and
cut-mind-units-install.r1 (gamecult-ops). Questions: mind-unit-names and
mind-unit-start-grant.
Revised again on 2026-10-22 by Imagination (imagination-self-run, session
self-2026-10-22-eureka-c2a) at Huginn 70d0cef, for cut mind-self-run. New facts F23-F25
(Claude Code session ids, resume and stream usage, probed on Starfire; the live-request half was
blocked by an expired CLI credential). The stale "Undone" list is replaced by "Cut sequence",
and "How a Self run is carried" is new. Specs: cut-mind-self-run.r1,
cut-mind-body-riders.r1. Questions: first-self-run-starter, self-run-permissions. Probe
outputs: F:\Projects\eureka-handoff-aux-2026-10-22\imag-self-run\.
Revised again on 2026-10-28 by Imagination (imagination-kit-lift, session
self-2026-10-28-eureka) at CodexConnector fd7cec4, Ghostlight c43eb54 and ModelConnector
1cf879f, for cut connector-kit-lift. New facts F26-F29, new rationale "Where the kit lift's
cut line falls", path B refreshed. Spec: cut-connector-kit-lift.r1. Questions:
connector-kit-relicense (blocks the spec), codex-connector-fate. The baseline probe log was
a session scratchpad file and is not kept; its counts are in F26.
Revised again on 2026-10-28 by Imagination (imagination-contract-v3, session
self-2026-10-28-eureka) at ModelConnector ec0519d and CultLib a4eae90, for cut
connector-contract-v3. New facts F30-F31 (probes on Yggdrasil), new rationale "Where the
contract v3 cut line falls", B3 split into B3a-B3c. Spec: cut-connector-contract-v3.r2.
Questions: connector-v3-wire, connector-v2-coexistence (both raised in r1, which r2
superseded only to split two context windows; they block r2). Follow-up:
cultnet-binary-operation-payload. Research: Huginn 725f5a5
docs/research/connector-contract-v3-prior-art.md.
Revised again on 2026-10-28 by Imagination (imagination-reasoning-item, session
self-2026-10-28-eureka) at ModelConnector bf958fc (B3a landed), for cut
connector-reasoning-item. New fact F32 (probe on Yggdrasil), the B3b bullet of "Where the
contract v3 cut line falls" made exact, path B line 3b given its spec. Spec:
cut-connector-reasoning-item.r1. No question: the ruling and research section 5 settle every
choice the cut makes. Research reused unchanged.
Revised again on 2026-10-28 by Imagination (imagination-connector-spend, session
self-2026-10-28-eureka) at ModelConnector 1d51e71 (B3b landed), for cut connector-spend.
New fact F33 (probes on Yggdrasil), the B3c bullet of "Where the contract v3 cut line falls"
made exact, path B line 3c given its spec and B4's riders carried. Spec:
cut-connector-spend.r1. Question: connector-spend-ceiling (blocks the spec). Follow-up:
connector-spend-readout. Research reused unchanged (725f5a5 section 2 is the ceiling
question's evidence).
Revised again on 2026-10-28 by Imagination (imagination-claude-connector, session
self-2026-10-28-eureka) at ModelConnector e0c0832 (B3c landed), for map line B4. New fact F34
(probes on Yggdrasil), rationale "Where the Claude connector cut line falls", path B line 4 split
into B4a-c. Spec: cut-claude-backend.r1 (B4a). Question: claude-connector-credential (blocks
the spec). Follow-up: claude-byte-bound-live-check. Research: Huginn f237623
docs/research/claude-connector-prior-art.md.
Revised again on 2026-10-29 by Modeling (modeling-claude-code-backend, session
self-2026-10-29-eureka) at ModelConnector 200933f, after B4a landed as claude-code-backend
(spec cut-claude-code-backend.r1; cut report h2, Soul verdict s2). Ruling
claude-connector-via-claude-code answered question claude-connector-credential and withdrew
spec cut-claude-backend.r1. New fact F35. The B4 rationale and path B line 4 are rewritten
(B4a landed, B4d added, the Console-key backend deferred), the spend-ledger row of the model
page changed and a Claude connector backend row was added. F34's repo-state sentence keeps its
tense.
Revised 2026-11-02 by Modeling (session self-2026-11-02-eureka) at Huginn ffd4136 and
gamecult-ops 477d0d2, with a read-only look at Yggdrasil the same day. Ruling
persona-session-landed replaced the one-shot Persona turn: the units rows, the Idunn sentence and
the "Swarm by 2026-10-30" flow are rewritten or marked historical, the section "The Persona session"
and the governance list are added, model-page rows for the session, sandbox, fence and doctrine
set are added, and facts F36-F40 are new. Claims the source note marks as inference or
unconfirmed keep that marking here.
Pinned heads (fetched 2026-10-08 ~12:10 UTC)
| Repo | Ref | Commit | Note |
|---|---|---|---|
| Huginn | origin/main = local main, F:\Projects\Huginn, clean |
6b496eb |
last commit 2026-10-02; deployed daemon is 107552d (docs-only diff to main) |
| Epiphany | origin/main |
ef956865 |
was Huginn's leaf pin (no pin since afc35f9); local checkout is on codex/eureka-pipeline-state 4006b14b. GitHub isArchived: false |
| CodexConnector | origin/main |
3ddc12e |
local checkout is on codex/ghostlight-release-binding 6519289; Ghostlight pins 68fe94b |
| gamecult-ops | origin/main |
7e23387 |
clean |
| Bifrost | origin/main |
160fac2 |
clean |
| Eureka (forge) | main |
0bb414d |
scratch clone, removed after |
| Idunn | origin/main |
6667f0f |
read only; not a campaign repo |
Body facts
Each line: what was run, where, result.
-
F1 Huginn never left Epiphany (at
6b496eb; cut atafc35f9, no build edge remains).grep epiphany crates/*/Cargo.toml(Huginn6b496eb):crates/huginn-mind/Cargo.toml:22pinsepiphany-pipelineby git at Epiphanyef956865.Cargo.lock:376-378carries the git source. The map-r4 cutleaf-into-huginnwas never specified; nocut_specfor it exists in any campaign (query of cut_spec headers undereureka-substrateandeureka-body). -
F2 Leaf users in Huginn.
pub use epiphany_pipeline;atcrates/huginn-mind/src/lib.rs:62; directuse epiphany_pipelinein huginn-mindadmission.rs:33(26 mentions),docs.rs:9,fixtures.rs:7,index.rs:10,157,mind.rs:16(9),query.rs:28,624,receipt.rs:19,50-52,314,refusal.rs:6,rows.rs:15,wire.rs:9; through the re-exporthuginn_mind::epiphany_pipeline::in eureka-statesrc/lib.rs:29,src/main.rs:25,tests/client.rs:17,tests/common/mod.rs:16,tests/mcp.rs:14,539; huginn-daemonsrc/daemon.rs:7,220,688,718,747,src/index/mod.rs:102,src/semantic_tests.rs:13,src/serve.rs:71,551. Prose:README.md:64,AGENTS.md:18. -
F3 The leaf at the pin (now Huginn
crates/eureka-pipeline,afc35f9). Epiphanyef956865:epiphany-pipeline/Cargo.toml(libepiphany_pipeline, deps anyhow, chrono, cultcache-rs at CultLib8fc74c7= Huginn's own CultLib pin, rmp-serde, schemars, serde; dev serde_json),epiphany-pipeline/src/lib.rs(schema-publication test readsCARGO_MANIFEST_DIR/../schemas/cultnetat lib.rs:2725).schemas/cultnet/holds 13epiphany.pipeline.*.v2schemas,epiphany.work_organ_state.v0,gamecult.persona_state.v0,index.json, README;schemas/README.md. -
F4 Mind deployment.
ssh yggdrasil:huginn.serviceactive, ExecStart/opt/gamecult/huginn/current/huginn-daemon --state-root /var/lib/gamecult/huginn --instance eureka --bind 10.77.0.1:17872 ...;current -> releases/107552dc...(Huginn merge of huginn-admission-rules; on 2026-10-08 main added docs only, and since then main has moved code: see F17). UFW admits 17872/udp on wg-gamecult from 10.77.0.2 only (runbookhuginn-yggdrasil.md:38,124). The daemon has no peer authentication (serve.rs: one session per peer,max_peers = 256); any reachable peer admits as any faculty. -
F5 Epiphany residue on Yggdrasil (12:13 UTC, before the teardown; what the teardown left is follow-up
eureka-body:follow_up:epiphany-body-leftovers). Unitsepiphany.service,epiphany-swarm.service,epiphany-heartbeat.serviceinactive/dead;epiphany-model-connector.serviceloaded, disabled. Usersepiphany(988; groups epiphany-state, bifrost-feedback-readers) andepiphany-model(984; group gamecult-model); no process runs as either. Roots and sizes:/srv/epiphany6.8G,/srv/epiphany-model-connector16G,/var/lib/gamecult/epiphany1.9G (body.cc, heartbeat.cc, codex-home, artifacts, capstones),/var/lib/gamecult/epiphany-model-connector28K,/etc/gamecult/epiphany(bifrost-persona-feedback-anchor.msgpack, credentials/, idunn-provider-projection-anchor.cc, mcp.toml),/etc/gamecult/epiphany-model-connector(connection.key). Manifests/srv/odin/deploy-manifests/epiphany,epiphany-capstone-17,epiphany.capstone-17.yggdrasil.yaml,epiphany-ox17,epiphany.ox17.yggdrasil.yaml,epiphany-swarm.service,epiphany.yggdrasil.Dockerfile./etc/gamecult/idunn/daemon-health-trust.cccontains the stringepiphany. About 24.7 GB in all. -
F6 What the live crossing still names.
/etc/systemd/system/bifrost-persona-mouth.servicecarries flags--epiphany-persona-mouth-trust-anchor,--epiphany-persona-permit-rudp,--epiphany-persona-permit-trust-anchor,--epiphany-runtime-id epiphany-starfire. gamecult-opsrunbooks/bifrost-persona-feedback-yggdrasil.md:17names/etc/gamecult/epiphany/bifrost-persona-feedback-anchor.msgpack,:45the installed/usr/local/sbin/bifrost-authorize-epiphany-release(sealed historical name),:67the feedback target--persona-id epiphany --repo GameCult/Epiphany --runtime-id epiphany-yggdrasil. These belong to the mouth rebinding, not to the teardown. -
F7 Nothing Epiphany runs on Starfire.
Get-Process,Get-ScheduledTask,Get-Servicefiltered on epiphany|eureka|persona|bifrost: empty. The permit issuer the crossing expects on Starfire is not running, so the mouth cannot obtain a permit today. -
F8 Idunn still lists Epiphany. Idunn
deploy/legacy/README.md:51-52lists targetsepiphany,epiphany-capstone-17;deploy/legacy/idunn-yggdrasil:126-135dispatch,:312,:321restart/deploy cases;deploy/legacy/idunn-yggdrasil.sudoers:10-11,19. The precedent for retirement is the same file's "Retired targets" (bifrost-persona-feedback, 2026-09-30). Idunn is not one ofeureka-body's repos. -
F9 Codex is dead in practice. Idunn
docs/migration.md:49-55: "The operator is not returning to Codex"; the deployed daemon failed with401 token_expired. On Yggdrasilcodex-connector.serviceis disabled, inactive since 2026-09-05 21:54 UTC, and nothing listens on 4103. Ghostlight's codex-connector library dependency is pinned at68fe94b, so a kit lift does not move Ghostlight. -
F10 CodexConnector at origin/main 3ddc12e (2026-09-13):
src/lib.rs1,958,daemon.rs1,180,provider_backend.rs1,463,idunn_health.rs1,396,main.rs188,deployment/idunn/recipe.toml. Contract types:CodexProviderRequestlib.rs:76-94,CodexInputItem:212-229 (UserText, AssistantText, ToolCall, ToolResult; no reasoning item),CodexTransportInvocation:238-245 (nomax_cost_usd),CodexTransportOutcome::Completed:449-462 (tokens, no cost),CodexRefusal:464-474 (noBudget). Schema idsgamecult.codex.*.v2lib.rs:51-55. -
F11 gamecult-ops Epiphany tooling, before the teardown (
git ls-files | grep -i epiphany, 7e23387):compose/epiphany.capstone-17.yggdrasil.yaml38,compose/epiphany.yggdrasil.Dockerfile55,runbooks/epiphany-yggdrasil-deploy.md196,runbooks/epiphany-starfire-bifrost-crossing.md101 (live crossing binding, kept), scriptsbootstrap-epiphany-yggdrasil.sh134,check-epiphany-yggdrasil.sh136,construct-epiphany-image-yggdrasil.sh91,deploy-epiphany-yggdrasil.sh499,deploy-epiphany-capstone-17-transaction-yggdrasil.sh5,deploy-epiphany-capstone-17-yggdrasil.sh79,epiphany-capstone-17-connector-bridge-yggdrasil.sh50,provision-epiphany-provider-health-yggdrasil.sh116,seal-epiphany-capstone-failure.sh67,test-deploy-epiphany-yggdrasil-order.sh144,test-epiphany-container-promotion.sh47,systemd/epiphany.service31,systemd/epiphany-swarm.service34; kept for the mouth cut:allow-starfire-epiphany-persona-permit.ps1,bifrost-authorize-epiphany-release.sh,configure-voidbot-epiphany-feedback-target-yggdrasil.sh. Inventory prose:inventory.md:581-588(Starfire as Epiphany body),:726-730(archived Verse residue),:773-785(intended Epiphany deployment body).docs/repo-census-2026-09/is history. -
F12 Eureka repo (forge main 0bb414d) carries the charters and doctrine (PR #3 landed). Epiphany citations remain:
README.md:63,86-87,95,SKILL.md:3,8-9,146, every charter's line 14 ("Self-improvement law (Epiphanyepiphany_doctrine.md)"),agents/hands.md:28,agents/soul.md:48,agents/life.md:24,doctrine/persona.md:3,13,doctrine/CLAUDE.md:31,229,233,doctrine/colossus.md:47,51(lore, kept). -
F13 eureka-substrate specs written against Epiphany.
cut-mind-authority.r1andcut-run-kind.r1have repoGameCult/Epiphany, baseef956865;cut-huginn-persona-state.r2basebf73c42;cut-doctrine-persona-in-huginn.r2gamecult-ops base2aeb7364. None has a report. Sinceleaf-into-huginnthey point at a repo nothing builds from;mind-authorityandrun-kindhave r2 specs against Huginn (see Undone). -
F14 Discord developer policy on training with message content: the policy page (support-dev.discord.com article 8563934450327) answered 403 to a fetch on 2026-10-08. Self's recollection, unverified: the policy forbids using message content obtained through the API to train AI models without express permission. An Eyes pass must verify before question
persona-trace-retentionis ruled. -
F15 Selection order is admission ordinal. CultLib
packages/cultnet-rs/src/selection.rs:1998-2005(order_rows: ordinal, then schema id, then record key; reversed when descending). So "newest" isdescending: true, limit: 1, and a run label needs no order of its own. -
F16 Huginn's leaf holds
leaf-key-bound.crates/eureka-pipeline/src/lib.rsatafc35f9:SUBJECT_LOCAL_MAX(:898),RESOLUTION_LOCAL_MAXderived from it (:912),Sha::names_same_commit(:272). The literal64at :915 (the root bound in the fits-Shortassert) is follow-upeureka-substrate:follow_up:leaf-bound-hygiene.leaf-key-boundlanded at Epiphanyef956865and came in with the leaf. -
F17 The live Huginn release is still
107552dc. Read-onlyssh yggdrasil readlink /opt/gamecult/huginn/currenton 2026-10-09:releases/107552dc4b27b8856b043616ae74c2ebe7908b66. Main (afc35f9) is ahead of it by the leaf move and the relicense; no deploy has happened since. -
F18 At Huginn
df7507bnothing stops a unit of hers. Read fromgit show df7507b:on 2026-10-09 (Imaginationimagination-brake-stop).crates/mind-bodyholdscontrol.rs,cli.rs,main.rsandlib.rs.cli::run(cli.rs:40-61) dispatchesshow,brake hold,brake releaseanddial set.brake holdisControlWriter::open(&path)?.set_brake(false, ..)(cli.rs:53) and does nothing else.git grep systemctlovercrates/mind-bodyfinds nothing. The binary sets umask 077 and callscli::runwith the rootCONTROL_ROOT(main.rs:21-37). The window probe runs the real binary withbrake releaseandbrake hold100 times (tests/control.rs:573-581), so a hold that ransystemctlwould run it in the verify container. Nolaunch.rsexists yet: the Launcher is cut mind-launch. RunTurn and RunOperator carryNAMES(crates/eureka-pipeline/src/lib.rs:507-508). -
F19 Yggdrasil has no unit, user or control store of hers. Read-only
ssh yggdrasilasgamecultadminon 2026-10-09:systemctl list-units --all 'mind-*'andlist-unit-files 'mind-*'both list nothing.ls /etc/gamecult/mindsgives "No such file or directory".command -v mind-controlfinds nothing.id mind-eurekagives "no such user".- The host runs systemd 257 (257.13-1~deb13u1).
systemctl --helplistsstop UNIT...and "--no-block Do not wait until operation finished", so a stop waits by default. systemctl stop --no-ask-password 'zz-probe-none@eureka:*.service'as non-root returned 0: a glob that matches no loaded unit is a no-op.systemctl is-active 'huginn*.service'matched by glob (active).- Operators act through
sudo: the gamecult-ops runbookhuginn-yggdrasil.md:86usessudo systemctl stop huginn, and/etc/sudoers.d/holds90-gamecultadmin.
-
F20 The ruling decides when the stop happens. Ruling
eureka-body:ruling:self-run-actuatorsays: "Holding the brake stops running units (systemctl stop; SIGTERM exits 143 with the turn unfinished, section 1.4)". It cites the OpenClaw inbox incident: a stop sent as a message was not acted on in time, and the process was killed at the host (prior-art file lines 323-325, secondary). Cut mind-wake r2 has the waker returnHeldbefore any query, admission or unit call, and lists "the waker ... ending a live run" among its forbidden writers. -
F21 What the mind can attribute a document to, at the mind-launch head
8dee875. Read from a throwaway worktree on 2026-10-09 (Imaginationimagination-launch-r3).- A receipt's provenance is
{ faculty, agent, session, tool }, all declared by the writer. No selection alias readssession, so attributing by session means scanning headers.open_and_launchadmits a run with session equal to its run label (crates/mind-body/src/launch.rs:205). Nothing makes a Self run's later admissions carry that label: the Self organ is not built. - Citations are typed content. A
citesselection with no role over every schema is supported and tested (crates/huginn-mind/src/query.rs:1499). A run's header facts carryturn,operated_byandclaims(query.rs:139). - The breaker at
8dee875scans every header in the mind, newest first, back to the oldest counted run (crates/mind-body/src/queue.rs:210). Its cost grows with the whole mind. Mind::admittakes&mut self(crates/huginn-mind/src/admission.rs:81), so admissions are serialized. The run rule isrun_claims_rule(admission.rs:537-547). It is applied when a run opens (admission.rs:526-529) and when a withdrawal reinstates a closed run (admission.rs:685-694). It checks claims only.MindRefusalis huginn-mind's (crates/huginn-mind/src/refusal.rs), published inschemas/cultnet/huginn.mind_response.v1.schema.jsonand pinned bypublished_wire_schemas_match_derivation(wire.rs:276). The leaf'sPipelineRefusalhas five variants (crates/eureka-pipeline/src/lib.rs:59-65) and is not where admission's rules refuse.Effective::Released { cadence, run_cap_usd }is a public struct variant (crates/mind-body/src/control.rs:117-120), so any caller can build one without reading the store.ControlState::effectiveis called only byread_effective(control.rs:192-197).- huginn-mind's
fixtures::runbuilds an in-force SelfRun withoperated_by: Mind(fixtures.rs:331-342). The claim tests open several of them at once (admission.rs:1987-2015). Slug::validate_slugandLabel::validate_labelare public (crates/eureka-pipeline/src/lib.rs:287, 386).RunTurnandRunOperatorspell their variants throughDebug, which is also theirNAMES(lib.rs:436-452).
- A receipt's provenance is
-
F22 What the mind units would land on, 2026-10-10. Read-only
ssh yggdrasilasgamecultadminand an Eyes pass over Idunn6667f0f(Imaginationimagination-mind-units).- Still no unit, user or store of hers:
systemctl list-unit-files 'mind-*'lists 0;getent passwdhas nomind-*user;/etc/gamecult/mindsis absent; nomind-controlon PATH./opt/gamecult/huginn/currentisreleases/107552dc...and holds onlyhuginn-daemon(0755 root). The Release step builds only-p huginn-daemon(gamecult-opsrunbooks/huginn-yggdrasil.md:57). - systemd 257 (257.13-1~deb13u1):
ExecCondition=(since 243) exists. Per systemd.service(5), an ExecCondition exit of 1-254 skips the unit (inactive, not failed) and 255 or a signal fails it; the+prefix runs that command with full privileges. - polkit is not installed:
polkitd"Installed: (none)", candidate 126-2;/etc/polkit-1/rules.dabsent (/usr/share/polkit-1/actionsexists, from systemd). Without polkitd, systemd refuses an unprivileged caller'sStartUnit, so no user of hers can start a unit today./etc/sudoers.dholds only90-gamecultadminand README. - systemd cannot derive a per-instance user from a
<instance>:<label>instance: no specifier splits%i, and dash-truncated drop-ins truncate the unit prefix (the part before@) and re-attach the whole instance (systemd v257src/shared/dropin.c,unit_file_find_dirs:unit_name_to_prefix,strrchr(prefix, '-'),unit_name_build_from_type(prefix, instance, ...)). SoUser=mind-%iworks only when the instance is the mind's slug alone. SystemdLauncher::alivecountsactive,activatingandreloadingas alive (mind-launch headorigin/eureka-body/mind-launch, launch.rs:96-99), and starts withsystemctl start --no-block, callingsystemctldirectly (no sudo).- Idunn has no target, binding or reference for huginn, mind-control or
mind-*(/etc/gamecult/idunn/bindings/holds ghostlight, heimdall, odin, raven-muninn and two streampixels). Idunn supervises only workloads it admitted and launched itself as transientsystemd-rununits (Type=exec,Restart=no,DynamicUser=yes; Idunnsrc/drivers.rs:3628-3650); its continuity loop restarts an admitted workload that is not running, up to 6 times an hour, unless its lifecycle brake is engaged (src/control_plane.rs~5880-6190, 7932-7956). It has no one-shot, timer or template workload (docs/watchdog-reporting-map.mdrow C9) and installs no unit file, polkit rule, sudoers file or system user (src/drivers.rs:3620-3720). A unit Idunn did not launch is never observed or restarted by it.
- Still no unit, user or store of hers:
-
F23 Claude Code session ids and resume, probed 2026-10-22 on Starfire. Claude Code 2.1.268 (Windows),
claude -p --output-format stream-json --verbose, run with the desktop host'sCLAUDE*/ANTHROPIC*variables removed (Imaginationimagination-self-run). Every call endedterminal_reason: api_error, "OAuth session expired and could not be refreshed": the CLI credential on Starfire is expired, so nothing below involved a model request, and every claim about cache reads on resume is still unprobed (see F25).--session-id <uuid>is honoured: thesystem/initevent, every event after it and the transcript file carry exactly that id. The transcript is<config dir>/projects/<cwd with separators as '-'>/<id>.jsonl, written even when the request fails.--session-idof an id that already has a transcript is refused before any event: "Error: Session ID ... is already in use.", exit 1. So a fresh run's id must be new.--resume <id>keeps the id: init and result carry the same id, and the resumed turns are appended to the original transcript file, from another working directory as well (the file found was under the first cwd's project dir; the appended lines record the new cwd).--resume <id> --fork-sessionstarts a new random id.--resume <unknown id>fails before the stream with "No conversation found with session ID: ...", exit 1, one line on stdout.--resumetogether with--session-idis refused: "--session-id can only be used with --continue or --resume if --fork-session is also specified." So a chain is: the first run--session-id <v5 of its own run key>, every continuation--resume <that id>.- Help text:
--input-format stream-json(realtime streaming input, print only),--replay-user-messages,--max-budget-usd,--max-turns,--permission-prompts host|none("none: anything that would prompt is denied automatically"),--no-session-persistence,--system-prompt-snapshot on(default: the system prompt is recorded on the first request and resent verbatim on every later request and resume).
-
F24 What one request's usage looks like. Read from a real Claude Code 2.1.268 transcript on Starfire (
~/.claude/projects/F--Projects-CultLib/<session>.jsonl, 736 lines, 64 assistant messages) and from the probe's stream (F23), which carries the sameassistantrecords.- One API request is one
message.id. A response with several content blocks is written as severalassistantrecords with the samemessage.idand an identicalusage(45 of 64 messages had 2 or more records; e.g.thinkingthentool_use, bothinput 2 / cache_read 45175 / cache_write 39033 / output 73). Summing records double counts; a reader keys onmessage.id. usagefields:input_tokens,cache_read_input_tokens,cache_creation_input_tokens,cache_creation.ephemeral_5m_input_tokens,cache_creation.ephemeral_1h_input_tokens,output_tokens,iterations,service_tier. The request's prefix isinput + cache_read + cache_creation; in that session it grew 84,210 to 287,837 over 64 requests.- A record from a subagent carries
parent_tool_use_id(set); the main thread's is null. Theresultevent carriesusage(session totals),modelUsage,total_cost_usd,terminal_reason,num_turns,subagent_statsandqueued_turn_count.
- One API request is one
-
F25 What the stream-json input channel accepts, read from the binary. Strings in Claude Code 2.1.268's
bin/claude.exe(npm@anthropic-ai/claude-code): 64 occurrences of"control_request"; a handlerif(d.request.subtype==="interrupt")that aborts the running turn and snapshots the command queue; an optioncancel_queuedon interrupt; the result schema'squeued_turn_count. So, by source read only: user messages written while a turn runs go to a command queue, and a control requestinterruptends the running turn. Unprobed: whether a queued user message is delivered mid-turn or after the turn'sresult, and whether closing stdin lets queued messages finish. The actuator spec is written so its correctness holds under either delivery, and its operator verification probes both on Yggdrasil with her token. -
F26 CodexConnector at origin/main
fd7cec4builds and tests green (2026-10-28, Yggdrasil, ygg-verify rust image,cargo build --features daemon --all-targets,cargo test --features daemon,cargo test): 46 lib + 1 bin tests withdaemon, 11 lib tests without.fd7cec4is F10's3ddc12eplus a README merge; F10's line numbers still hold. Files:src/lib.rs1,958,daemon.rs1,180,provider_backend.rs1,463,idunn_health.rs1,396,main.rs188, novendor/. CultLib pinned ate171eca(2026-09-04); 248 CultLib commits touchpackages/cultcache-rsorpackages/cultnet-rssince, and the store API the connector uses (OwnedRedbMessagePackBackingStore,SingleFileMessagePackBackingStore,register_entry_type,pull_all_backing_stores,get_all_with_keys) still exists at CultLiba4eae90. -
F27 What is provider-neutral and what is Codex (source read,
fd7cec4). Neutral: the contract types and validation (lib.rs:51-485,622-631,1351-1415), the AES-256-GCM envelope and four-byte big-endian framing (633-710,824-952), the one-request client (711-833), caller admission and the CultCache replay service (954-1313), and the accept loop (daemon.rs:344-380,565-740). The daemon calls its backend at exactly two points,readiness()andexecute(&invocation) -> CodexTransportResult(daemon.rs:592-653), so one trait is the whole seam. Codex:provider_backend.rs(the pinnedcodex app-serverchild,auth.json, Responses SSE parsing), the Responses loweringcanonical_responses_body(lib.rs:487-620),CodexDaemonConfig(daemon.rs:47-171, CultCache typegamecult.codex.connector_config.v1, withcodex_executableandcodex_home), the managed paths (daemon.rs:453-536), and the Idunn recipe (npm@openai/codexinput).idunn_health.rsis Idunn runtime presence with Codex-named constants (targetcodex-connector, capabilitygamecult.codex.subscription-inference, envCODEX_CONNECTOR_CULTNET_RUDP). The request itself carries OpenAI Responses fields (reasoning_summary,service_tier,previous_response_id,output_format_name,prompt_cache_key). -
F28 The wire is serde MessagePack, not CultNet (source read). The envelope, invocation and result are
rmp_serdeencodings of serde structs (lib.rs:476-710, client748and765, daemon637and652); schema ids are strings compared by hand, not a CultNet schema. CultCache holds only the replay record and the daemon config; CultNet is used only for Idunn runtime presence and route observation. JSON:parameters_json,output_schema_jsonand toolargumentsare JSON strings checked byrequire_json_object; they are provider-facing (JSON Schema and the provider's own tool-call arguments), a xenos boundary. Every other JSON use is insideprovider_backend.rs(HTTP body, SSE, app-server JSON-RPC,auth.json), also xenos. -
F29 Consumers and license (git grep at each origin/main). Ghostlight
c43eb54pinscodex-connectorat68fe94bas a library (Cargo.toml:18, nodaemonfeature) and imports the contract and client incrates/ghostlight/src/controllers.rs:34-37and seven other files; Epiphany's pins are archive (target invariantnothing-runs-from-epiphany). gamecult-opsidunn/yggdrasil/bindings/codex-connector.toml.innames origin GitHub CodexConnector; Idunndocs/migration.md:49-55says retire codex-connector, and:163calls its recipe stale. CodexConnector's project code is PolyForm Noncommercial 1.0.0 (LICENSEsection 3), all 33 commits by Meta Vens; ModelConnector is AGPL-3.0-only at1cf879f(LICENSE and a 26-line planned README). -
F30 The v2 wire decoded, and what a v3 change costs it (2026-10-28, Yggdrasil, ygg-verify rust image, scratch clone of ModelConnector
ec0519dplus a probe-only commit; research725f5a5section 4, probes P2-P5). Baseline 77 tests green with and withoutdaemon. The real v2 frameenvelope_invocation.mpis 735 bytes for 429 bytes of ciphertext (ratio 1.713; a positional six-array,0x96) and decrypts to Ghostlight-shaped content. Ciphertext as MessagePackbinmakes it 526 (1.226); at 1 MiB all-high, v2 2,097,251 vs bin 1,048,675. The v2 type reads bin and named encodings unchanged. Positional encoding fails on any added field ("array had incorrect length"); named encoding drops an unknown field silently; a v2InputItemrefuses an unknown variant; a v3 envelope id is refused by v2 before decryption, so a v2 daemon drops a v3 caller's connection and never answers. -
F31 CultNet can carry the kit as an operation service (same job, cultnet-rs at CultLib
a4eae90; source reads in research section 4). The sealed envelope as themessagepack-base64payload ofcultnet.operation_request.v0round-trips throughencode_cultnet_message_to_vec/decode_cultnet_message_from_slice: 1.354x at 64 KiB, 1.335x at 1 MiB, 1.334x at 8 MiB (deterministic), 6.5 ms encode and 1.0 ms decode at 1 MiB, release. CultNet'sTcpFramedframe is the kit's 4-byte big-endian frame; operation payloads may only bemessagepack-base64(contracts.rs:780); CultNet security holds one key per server where the kit binds one per caller with ids in the AAD. Huginn'shuginn.mindis already this shape (huginn-mind/src/envelope.rs). -
F32 The reasoning variants cost the v3 wire nothing it has pinned (2026-10-28, Yggdrasil ygg-verify rust image, scratch commit
af9fb74on ModelConnectorbf958fc, never pushed).InputItem::ReasoningState { provider, model, state }andTransportEventPayload::ReasoningState { state },stateunderserde_byteswith the envelope'sbinary_schema: all 83 unit tests and bothwire_golden_v3tests pass with every committed golden byte-identical; onlypublished_schemasfails (the new variant, expected). The item encodes as a one-key map whosestateis MessagePackbin(c4 03 ff 80 01for three bytes); a 1 MiB state encodes in 1,048,623 bytes (47 bytes of overhead); the schema showsstateasformat: binary. So the receipt fieldforeign_reasoning_droppedand the new event in the transported golden are the only wire changes B3b makes to existing fixtures. Source reads atbf958fc:ProviderBackend::executereceives the caller's wholeTransportInvocation(serve.rs:29, viaExecutionClaim::invocation,service.rs:115-119), and every backend writes its ownreceipt.provider(fixtures.rs:63-83, test backendsserve.rs:945-962,service.rs:1074-1091): today nothing stops a backend from seeing foreign state or mis-tagging its own. -
F33 The kit holds no spend today, and its store fails closed but locks out readers (2026-10-28, Yggdrasil ygg-verify rust image, scratch commit
79e14b8on ModelConnector1d51e71, never pushed; source reads at1d51e71). Baseline: 94 lib tests withdaemon, 37 without, plus 4published_schemas, 4reasoning_stateand 3wire_golden_v3, all green. P1: aCacheBackingStorewhosepushfails makesCultCache::putreturn the error and leaves nothing in memory (getisNone), so a reservation written withputbefore execution is either durable or refused.beginalready returnsServiceError::ReplayStorebeforelive.insertand before the backend runs (service.rs:367-369), so the replay store is a fail-closed ledger by construction. P2: a secondOwnedRedbMessagePackBackingStore::newon a held path fails "already has an active owner", in the same process and from a child process, so no root stop hook can read the connector's store while the connector runs. Source reads:CallerConfigandCallerAdmissioncarry no spend field (serve.rs:105-114,service.rs:19-28);Usageis tokens only and the receipt has no cost (contract.rs:193-204,465-479);TransportOutcome::Failedcarries no usage (524-527); replay records are never deleted or pruned (nodeleteon the store inservice.rs);TransportService::opentakes a path, so a test cannot inject a failing store (service.rs:181-182). -
F34 The kit's in-flight boundary misses Claude's turn rule; caller JSON must not pass through
json!(2026-10-28, Yggdrasil ygg-verify rust image, scratch commitseb021f0,0c9a39don ModelConnectore0c0832, never pushed). P1: a kit lib test admitting[UserText, ReasoningState(other-provider), ToolCall, ToolResult, UserText]printedexecuted, dropped=1, so foreign state in a tool exchange the model has not yet answered is dropped, where Anthropic counts a tool loop as one assistant turn (researchf237623section 4). P2: aRawValue{"b":1,"a":2}serialized throughjson!came out{"a":2,"b":1}; through a typed#[derive(Serialize)]field it came out verbatim. P3:ureq 3.5.0(rustls, no default features) withserde_jsonraw_valuebuilds in the workspace on rust 1.95. Baseline: 138 kit lib tests withdaemon, green. Source reads ate0c0832:ProviderBackend::executereturns aTransportResultsynchronously on the serve loop's per-connection thread (serve.rs:25-38,240-262); the backend's only result door isProviderCall::transported(service.rs:380);wire_encodeispub(crate)(contract.rs:586), so a connector crate encodes its own state withrmp_serdedirectly; there is no connector binary, config document or Idunn presence in the repo (crates/holds onlymodel-connector); CodexConnector'sidunn_health.rsis 1,396 lines and its recipe binds a CultCache config document, a replay slot and a credential state slot (deployment/idunn/recipe.toml). -
F35 B4a at ModelConnector
200933f(landed; read from cut reporth2, verdicts2and the crate, 2026-10-29).crates/claude-connectoris a library (backend.rs,config.rs,handle.rs,launch.rs,process.rs,stream.rs; depsmodel-connectorwithdaemon,serde_json,rmp-serde,sha2,zeroize,libcon unix; no HTTP client). There is still no binary, config document or Idunn presence.model-connectorandschemas/are byte-identical toe0c0832. Gate: 198 tests green;cargo mutants --in-diff155 mutants, 121 caught, 0 missed, 2 timeouts (process.rsStdChild::kill). Live (claude 2.1.312, Starfire, haiku-5-5, four calls): print mode emitsresult.model_usage_since_last_result, the turn's own usage; the cumulativemodelUsageof a resumed fork also counts the earlier turns, so it is never read;--system-prompt-filereplaces the system prompt as--system-promptdoes. Argv sets--tools "",--strict-mcp-config,--setting-sources "",--permission-prompts none, and--resume <id> --fork-sessionon every resume. The child env is cleared toPATH,HOME,CLAUDE_CONFIG_DIR,CLAUDE_CODE_MAX_OUTPUT_TOKENSand the one constCLAUDE_CODE_OAUTH_TOKEN. Not exercised live:CLAUDE_CODE_MAX_OUTPUT_TOKENS. Open scars from Souls2:stderr-join-reachable(the stderr-tail join is reachable but undefended in the repo),prompt-file-exclusive-undefended(the exclusive create on a predictable temp name), anddoor-refusal-boundary(code bills zero only for a 401 or 403 with explicitly empty usage; rulingclaude-code-plan-quota-fuse-2, admitted afters2, says 401, 403 and 429, so a 429 with{}still bills the reservation until follow-upclaude-backend-hardeninglands). Follow-upshandle-unauthenticated,pin-checked-only-at-ready. -
F36-F40 (Modeling, 2026-11-02, Yggdrasil read-only). F36
ssh yggdrasil2026-11-02: units active:mind-fence,mind-heartbeat,mind-persona@{eureka,kiko,nibu,void,weksa},mind-self@{eureka,heimdall,kiko,void},huginn.service(eureka) andhuginn@{aqua,heimdall,kiko,libby,nibu,void,weksa}. Deploy/opt/gamecult/huginn/current->releases/ffd4136a35355587a1a6d04c3cc42d8fabc31e86= Huginn main head. F37systemctl cat mind-persona@.service:Requires/After=mind-fence.service,User=mind-%i,RuntimeMaxSec=4h,Restart=no,KillMode=control-group,ProtectSystem=strict, seedpersona-seed. F38nft list table inet mind_fence: 8 persona uids, 8 uid.port huginn pairs (17872-17879), chainpersonawith reject rules. F39/opt/commonsmodes as in the Persona session section;id mind-eurekashows nomind-commons;systemctl cat mind-self@.serviceshowsSupplementaryGroups=claude-lease mind-commons,ReadWritePaths=/opt/commons. F40grep pombabranca /etc/nginx/sites-enabled/*: no match;/siteand/site/locations present inyggdrasil.gamecult.org.conf(lines 50, 54). Retired/pombabranca(7586a3f). A pre-ghostlight backupsites-available/yggdrasil.gamecult.org.conf.before-ghostlight-*still names/pombabrancabut is not enabled.
The model page (step 0b)
One row per persistent kind this body introduces or changes. Rows for the thirteen
pipeline kinds are in Huginn docs/eureka-substrate-map.md.
| Kind | Identity (what names it) | Lifecycle (what happens over time) | Authority (who decides) |
|---|---|---|---|
| Pipeline wire ids | epiphany.pipeline.<kind>.v2, epoch epiphany.pipeline.epoch.v2, defined by the leaf, the Huginn crate crates/eureka-pipeline (lib eureka_pipeline), ids unchanged. |
Renamed once to eureka.pipeline.<kind>.v3 by one mind.redb migration, at the next breaking leaf change or before the first hosted mind (ruling wire-names-rename-before-hosting). Adding the run kind or the Mind authority variant is additive (old stored documents decode; no migration), so it does not trigger the rename; a field added with the leaf's = absent marker is additive too (leaf-read-anchors r2: old bytes decode, empty fields encode byte-identically, golden/envelopes.txt gains lines and loses none); a field added any other way would. |
Huginn owns the leaf. The migration is a live-mind upgrade, which is the operator's (mind-rules-which-forks). |
| Leaf crate and schema catalogue | Huginn crates/eureka-pipeline, schemas/cultnet/ (13 pipeline schemas, gamecult.persona_state.v0, index, README). epiphany.work_organ_state.v0 dies: no reader outside Epiphany (grep, F1/F3). |
Landed at Huginn afc35f9 (leaf-into-huginn). Wire pinned by crates/eureka-pipeline/golden/envelopes.txt and huginn-mind's opt-in query::tests::stored_documents_read_back. Huginn is AGPL-3.0-only (ruling eureka-body:ruling:leaf-relicensed-agpl). |
Huginn. Forbidden: any Cargo source resolving into GameCult/Epiphany (nothing-runs-from-epiphany). |
run |
<instance>:run:<label> (cut-run-kind r2). Label mind-<YYYYMMDD>T<HHMMSS><mmm>Z from mind-launch's run_label; two launches in one millisecond produce the same key with different content, which admission answers Conflict, so the name stays injective. Order is admission ordinal (F15), not label. |
The heartbeat opens a PersonaTurn run with no claims each time it launches a Persona turn (a mind-persona start that finds no such run opens none and runs nothing) (ruling eureka-substrate:ruling:every-wake-a-run); the Persona tool opens SelfRun runs with claims; both go through open_and_launch, and the unit starts only after the run commits (the run is the grant). The Self in a unit admits under its label and ends it Recorded { reason }. A run whose unit is not alive past a 120 s start grace is closed Recorded { reason: "unit not active" } by the next open_and_launch of the same turn, before it checks anything else. It is Recorded, not Withdrawn, so a unit that keeps dying counts as an empty ended run and trips the breaker. Only a unit whose start failed in the launch itself is Withdrawn. Idle time comes from the newest PersonaTurn run's admitted_at (any status), not started_on (a date cannot carry seconds); there is no cadence, the heartbeat's initiative timeline decides when a Persona wakes. |
Opening: open_and_launch, called by the heartbeat for PersonaTurn and by the Persona tool for SelfRun, each with a Grant that only read_effective constructs. One live run of hers per turn and instance: huginn-mind admission refuses a second in-force run with operated_by: Mind of the same turn and instance as AlreadyLive { run } (ruling one-live-self-run, option one-live-per-turn; cut run-live-admission). open_and_launch answers Busy from that refusal, or earlier, when the holder it has just read is alive. Operator runs never count. operated_by is declared by the writer (follow-up eureka-substrate:follow_up:operator-authority-needs-operator-credential). Ending: the Self that worked it; a dead one is closed by the next launch of its turn (cut mind-launch r3). No sweep exists elsewhere. Forbidden: Hands, Soul, Imagination, Life. |
| Repetition breaker | No state of its own (rulings self-waker, merge-gate-own-soul). Trip condition derived from one selection: the documents citing the item, in any role. Counted runs are the item's ended SelfRun runs of hers (operated_by: Mind, not withdrawn) admitted after the newest question raised_in the item. Trip when the last 3 counted runs are empty, or 10 of the last 50. A run is empty when no citing document other than a run has an ordinal in its window (its admission up to the next run's, or now). Documents that cite something else never count, wherever they were admitted (F21). |
Evaluated in open_and_launch when a Self run claims the item, not by the heartbeat. When tripped, it admits one question labelled breaker-<run label> under the item's campaign, raised_in the item, and launches nothing. The run label is the label the refused launch would have used, so every trip has its own key. The existing blocked-spec recipe then removes the item from the queue. Answered by a ruling or withdrawn: either reopens the item, and that question is the boundary, so a reopened item starts a fresh count. |
open_and_launch admits the question; any Self may answer it (substrate-internal, mind-rules-which-forks). |
| Brake | One document per instance in a root-owned CultCache store /etc/gamecult/minds/<instance>/control.cc, type eureka.control.brake.v1 { released: bool, set_at, set_by }. |
Replaced whole by the operator's CLI; never appended. Absent or undecodable reads as held. mind-control brake hold writes it held and then, in the same command, stops her running units: systemctl stop (blocking) over mind-self@<i>:* and mind-persona@<i>:*, through SystemdLauncher::stop_all (cut brake-stop; ruling self-run-actuator; F18-F20). |
Operator only (ruling brake-dial-root-store-now); her units read it, and the file is not writable by any user she runs as. The hold is the one stopper of her units. read_effective is the one reader. Its Released answer carries a Grant (cadence and run cap, private fields, not Clone), which open_and_launch takes by value, so no run opens without a released reading (cut mind-launch r3). |
| Swarm dial (supersedes the per-instance burn-rate dial) | CONTROL_ROOT/swarm.cc only; the per-instance control.cc holds the brake and nothing else. eureka.control.burn_rate.v1, Grant.cadence and base_cooldown_s are deleted (cut mind-heartbeat). |
Operator CLI mind-control swarm .... |
Ceilings: heat 2, base rate 100 USD/h, base run 50 USD, k 16. |
| Connector request and transcript items | Request: (caller_runtime_id, request_id), the replay key the daemon already keeps (replay.cc). Items: UserText, AssistantText, ToolCall, ToolResult, plus ReasoningState { provider, model, opaque } (ruling connector-opaque-state-item). Contract ids become provider-neutral gamecult.model.*.v3 (follow-up connector-contract-untyped); the connector serves Ghostlight too, so not eureka.*. |
Invocation expires at expires_at_unix_ms; replay record per key; restart-era Indeterminate. The caller stores the transcript and replays it verbatim; foreign-tagged reasoning state inside an in-flight tool exchange is refused typed; completed-turn foreign state is settled per provider in the connector cut (follow-up connector-reasoning-state-cheap-providers). |
The connector produces, reads or refuses reasoning bytes; callers never branch on provider. |
| Connector spend ledger | The reservation lives in the replay record itself (cut connector-spend, F33): integer micro-USD, windowed per caller (ruling connector-spend-caller-window-ceiling). |
Reserve before the provider runs, settle to actual; refusals CostBound (the invocation's own max_cost_micro_usd) and SpendCeiling (the caller's window); a ledger that cannot be written refuses the request. |
The kit's ProviderCall::charge is the one billing owner; a backend reports usage or None (bills the reservation). The root caller config sets the ceiling. |
Claude connector backend (B4a, landed 200933f; the Persona does not use this backend, it borrows a lease like the Self) |
ClaudeCodeBackend in crates/claude-connector, provider tag claude-code: one claude -p child per call in its own process group, tool-less, system prompt in a private temp file; reasoning state is a named-MessagePack ClaudeCodeHandle { session_id, instance, prefix_sha256 }; usage is model_usage_since_last_result summed over models, in list-price units from B4b's price table (ruling claude-code-plan-quota-fuse-2). |
A handle resumes (always --fork-session) only when it decodes, names this instance, its session file exists and its digest matches the request's instructions, model and earlier items; anything else re-seeds from the caller's transcript (ruling claude-code-handle-reseeds). The CLI's session store under config_dir is a cache; the caller's transcript is the truth. A refusal at the door with explicitly empty usage bills zero; unknown usage, a crash or a deadline bills the reservation. |
The backend owns the argv, env, process and stream mapping. It never reads total_cost_usd, never forwards ANTHROPIC_API_KEY, never retries, never spawns twice per execute. Forbidden: --bare, --fallback-model, --mcp-config and --dangerously-skip-permissions (B4d changes the MCP line, by spec). Her organs hold none of it: the subscription token lives in the connector's own environment. |
| Persona turn traces | Empty: question persona-trace-retention. Candidate: per turn, the request digest, the transcript items and the typed output, keyed by the turn's run. |
Empty: how long they are kept, whether Discord-sourced content may enter a training export (F14). | Empty: who may export them. Unconfirmed 2026-11-02: FileTrace/TurnTrace exist in persona.rs; whether they are the retention answer is not checked against the question's state. |
| Persona state | gamecult.persona_state.v0, one per mind, personaId = instance (cut-huginn-persona-state r2, eureka-substrate). |
Replaced whole under CAS with a receipt; she may rewrite any field (identity-write-all-visible). |
The Persona organ is the only writer. |
| Persona session | Process claude under bwrap in mind-persona@<i>; id uuid5(run, p<n>); one conversation per process |
Ends at cache TTL, near the 4 h cap minus ROTATE_MARGIN_S, at the context ceiling's flush, or when another conversation's delivery is the oldest unanswered; next process rehydrates from the mind |
The Persona organ (lifetime, input, acks); the mind is truth; heartbeat only starts units |
| Persona sandbox | /var/lib/mind-<i>/sandbox |
Kept between processes; the only place her tools write (besides the fresh config dir) | Her; nobody else reads it. Not a source of truth for anything. |
| Mind fence | nft table inet mind_fence |
Reloaded at every Persona start; absent after any nft flush until then | gamecult-ops |
| Persona doctrine set | MIND_PERSONA_DOCTRINE |
Changed only by an installer run at a new skill pin | gamecult-ops installer + Eureka skill repo |
Mind web builds (cut-minds-web.h4; gamecult-ops 6564bd4, 0c0ee90) |
/srv/minds-web/<mind>/<build>/, served as https://minds.gamecult.org/<mind>/<build>/. Live 2026-11-02: root root:root 0755, eight folders (aqua, eureka, heimdall, kiko, libby, nibu, void, weksa) each mind-<m>-self:mind-<m>-self 0755, all empty; vhost /etc/nginx/sites-enabled/minds.gamecult.org.conf present; /eureka/ answers 404 (no build yet). |
A Mind's Self run writes a build folder; nginx serves it static: no proxy, no cookie, no CSP, no autoindex, disable_symlinks if_not_owner, dotfiles 404, one location for every Mind (a new Mind needs a folder, not an nginx edit). |
The Mind's own Self unit alone (ReadWritePaths=-/srv/minds-web/<m> in mind-self@<m>.service.d/endpoint.conf, seen for aqua, eureka, heimdall, kiko; no Persona drop-in names minds-web). The installer owns the folders; no Mind writes another's folder; root-owned /srv/cinny stays refused (a chat-origin preview slot was refused: a Mind's script would share the chat client's origin). Void's beta is /srv/minds-web/void/; the /srv/site grant was dropped (b970917). |
| Identity diffs | The pair of receipts of two consecutive Persona puts whose identity fields (publicName, publicDescription, presentation, values) differ. Derived, not stored. | Composed by the Persona organ at put time and sent as one delivery request through the crossing; a failed post is retried by Bifrost's delivery journal, not by her. | Persona organ composes; Bifrost posts under the permit. No rate limit. |
| Discord body surfaces | Outbound: crossing ids epiphany.persona_discord_delivery_request.v0 and ..._permit.v0, agent epiphany.Persona, runtime epiphany-starfire (F6), renamed to eureka.* and eureka-yggdrasil in the mouth rebinding (mouth-crossing-on-yggdrasil). Inbound: Bifrost persona-feedback deliveries for target epiphany (F6). Persona-session tools reach both (body-includes-discord). |
Request, permit, post, receipt (Bifrost journal). Inbound deliveries: unprobed who records that a delivery was answered; run-is-the-grant forbids her organ from holding that fact. Blocks the Persona cuts only, not the cuts admitted today. |
Bifrost owns posting and delivery records; her permit issuer grants while the brake is released. |
| Units and instance templates | mind-heartbeat.service (one, not per instance), mind-self@<instance>:<label>.service, mind-persona@<instance>.service (instance only, never :<label>; a long-lived leased claude session, capped by RuntimeMaxSec=4h and MIND_PERSONA_RUN_CAP_S=14400), mind-fence.service (one); users mind-<instance> (organs) and mind-<instance>-self (Self runs); state /var/lib/gamecult/minds/<instance>/; control /etc/gamecult/minds/<instance>/. Instance eureka. |
Hand-installed by gamecult-ops now; Idunn targets declared before the first hosted member, which deletes the hand install (mind-units-templated-interim). |
gamecult-ops installs; Idunn later. Starting: open_and_launch through SystemdLauncher::start, as her users, who are granted start only. The instance in a unit name is always the mind's (MindPort::instance), passed to every Launcher call; SystemdLauncher holds none. The instance and label are validated by the leaf's grammar before any systemctl argv is built. Stopping: only the operator's brake hold, as root (cut brake-stop). The unit-level brake gate and the start-only grant are follow-up mind-units-brake-gate. None of this exists on Yggdrasil yet (F19, F22). Each piece's identity, lifecycle and authority: the section "The mind units" below (cut mind-units-install; unit names pending question mind-unit-names). |
| Self-run credential | None in her unit. The connector holds the one subscription token; a Self run borrows it as a lease over a unix socket (CLAUDE_LEASE_SOCKET), and the token reaches claude only as a descriptor it reads once (CLAUDE_CODE_OAUTH_TOKEN_FILE_DESCRIPTOR). The child's environment is cleared and rebuilt from an allow-list (PATH, LANG, LC_*, TERM, and its own HOME and CLAUDE_CONFIG_DIR), so no credential variable in the unit can outrank the lease. |
Rotated by the operator at the connector, never in her unit: no EnvironmentFile carries a token for mind-self@ (rulings self-runs-through-connector, self-run-credential-lease; self-run-token-reach is superseded). |
The connector's: one lease per unit invocation, bound to the peer; the unit holds no credential variable. Her claude runs her tools, so anything in the unit's environment is readable from /proc/<claude>/environ by her Bash (ruling claude-code-runs-her-tools-2). Since persona-session-landed the Persona unit borrows the same lease (SupplementaryGroups=claude-lease; lease row PersonaTurn bound to mind-persona@<m>). |
| Wake frame | Fixed prompt frame (provenance, jurisdiction, brake obedience) in /etc/gamecult/minds/<instance>/wake-frame.md, root-owned. Composed by the Persona organ from host config; the waker passes the unit only the instance and the run label. |
Changed only by an install. | Host config (ruling self-waker rider 2); never a file her runs merge into. |
| Epiphany residue | F5, F8, F11, as they stood before the teardown. | Teardown landed (gamecult-ops 9c12adb); what it did not name is follow-up eureka-body:follow_up:epiphany-body-leftovers; the repo is archived last. |
gamecult-ops for host and repo tooling; Idunn for its legacy target list (follow-up). |
The mind units: identity, lifecycle and authority
Written 2026-10-10 before cut mind-units-install was mapped (operator direction: settle
ownership first). <i> is the mind's slug (eureka now). Unit names follow the
recommended option of question mind-unit-names (instance-only): the instance is the slug
alone, one unit per instance and turn, because admission already allows only one live run of
hers per turn (one-live-self-run) and only then can User= name a per-instance user (F22).
"Root" means the operator through sudo, or an agent under her deploy instruction.
| Thing | Identity | Lifecycle | Who may start, stop, enable or change it |
|---|---|---|---|
| Heartbeat unit | One mind-heartbeat.service (not per instance; mind-wake@ timers and units are retired). Needs HEARTBEAT_PERSONAS (each entry ends with that Persona's own Huginn rudp://<ip>:<port>: each Mind is its own daemon, ruling swarm-one-connector-many-callers-mind-per-daemon), MIND_PERSONA_OPERATOR, BIFROST_CHAT_ENDPOINT and _KEY_FILE, MIND_CONNECTOR_ENDPOINT, _KEY_FILE, HEARTBEAT_CLASSIFIER_MODEL, STATE_DIRECTORY. |
Idunn-managed, sole writer of heartbeat.cc. |
Install, replace, restart. |
| Persona template | /etc/systemd/system/mind-persona@.service. Instance unit mind-persona@<i>.service (instance only, never :<label>), User=mind-%i, Type=exec, no [Install], so it cannot be enabled. Requires= and After=mind-fence.service, with ExecStartPre=+systemctl reload mind-fence.service; SupplementaryGroups=claude-lease; LoadCredential=chat-key; MIND_PERSONA_CONFIG_SEED=/etc/gamecult/minds/%i/persona-seed; ProtectSystem=strict; KillMode=control-group. |
Inactive until the heartbeat starts it. It carries one long-lived leased claude session (not a one-shot turn) and ends by its own exit, at the cap (RuntimeMaxSec=4h, MIND_PERSONA_RUN_CAP_S=14400), or by the brake hold's stop (TimeoutStopSec=30s, then SIGKILL to the whole cgroup). Restart=no, and nothing else restarts it. Every start first runs the gate (below); while the brake is not Released the unit is skipped and ends inactive. ExecStart names the Persona organ's binary in the Huginn release, which ships (live, read 2026-11-02: five units active). |
Start: mind-<i> only, through open_and_launch (the waker), by polkit's verb start on this one unit name. Stop, kill, restart: root only, which in practice means mind-control brake hold (cut brake-stop). Change: root via the script. Enable: nobody. Session, sandbox and fence: the section "The Persona session" below. |
| Self template | /etc/systemd/system/mind-self@.service. Instance unit mind-self@<i>.service, User=mind-%i-self, Type=exec, no [Install]. |
As the Persona unit, with RuntimeMaxSec=4h. ExecStart names the Self-run actuator's binary in the Huginn release; that cut adds the state directory, RuntimeDirectory= (each child's fresh configuration), MIND_SELF_RUN_CAP_S equal to RuntimeMaxSec in seconds, CLAUDE_LEASE_SOCKET and the argv. It adds no token and no EnvironmentFile with a credential. |
Start: mind-<i> only (the Persona's start-Self-run tool through open_and_launch), by polkit, verb start. Stop, kill, restart: root only. Change: root via the script. Enable: nobody. |
| Organ user | mind-<i>: system user and group, shell /usr/sbin/nologin, home /nonexistent. |
Created by the install script if absent and never modified by it after that. Deleted by root at the mind's retirement. Runs the waker and Persona units. | Root creates and deletes it. It may start mind-persona@<i> and mind-self@<i>, and nothing else. It cannot write the control store (root 0644, dir 0755) or stop a unit. It can signal its own processes with kill(2), as any Unix user can. That only ends spend, and the ended run is closed Recorded by the next launch, so it adds no second stopper of her units. |
| Self user | mind-<i>-self: system user and group, nologin, home /nonexistent. The install script refuses an instance slug ending in -self, so this name never collides with another mind's organ user. |
As the organ user. | Root creates and deletes it. It starts no unit. Polkit refuses it every verb, and the rule refuses any subject whose name ends in -self. |
mind-control |
/opt/gamecult/huginn/releases/<sha>/mind-control, root:root 0700, reached by the current symlink; /usr/local/sbin/mind-control links to /opt/gamecult/huginn/current/mind-control. |
Built and installed with each Huginn release by the Release step of runbooks/huginn-yggdrasil.md. Replaced when current flips. The previous release is kept until the new one verifies. |
Install, replace: root via the Release step. Run: root only (mode 0700), which covers the operator's verbs and the gate, which systemd runs as root through the + prefix. No user of hers can execute it. |
mind-heartbeat |
/opt/gamecult/huginn/releases/<sha>/mind-heartbeat, root:root 0755. |
Owner of initiative: reads the swarm envelope, the mind (busy, idle, run ends), the mouth (typed sender, m.mentions, room) and the connector (classifier urgency, spend); writes heartbeat.cc (Initiative per instance, poll traces) and launches Persona turns through open_and_launch. The operator tagging or naming a Persona fills its initiative once per message; a member never does. |
mind-heartbeat timeline prints the latest poll. |
| The gate | ExecCondition=+/opt/gamecult/huginn/current/mind-control --instance %i check in both spending templates. The verb is cut mind-unit-gate. |
Runs at every start of a Persona or Self unit. It exits 0 only when read_effective is Released; held, absent, undecodable or out of bounds exit 2 (systemd skips on 1-254). It holds no state and writes nothing. |
Owner: read_effective, the one derivation. Change: a Huginn release (the verb) or the script (the line). It closes the window in which a launch that read Released before a hold starts its unit after the hold's stop (follow-up mind-units-brake-gate). |
| Polkit rule | /etc/polkit-1/rules.d/60-mind-units.rules, root 0644, source polkit/60-mind-units.rules in gamecult-ops. Needs package polkitd (absent today, F22; question mind-unit-start-grant). |
Installed and replaced by the script. Instance-generic: one file serves every mind. | Root changes it. It grants exactly one thing: action org.freedesktop.systemd1.manage-units, verb start, unit mind-persona@<i>.service or mind-self@<i>.service, to subject mind-<i>. Every other request falls through to systemd's default, auth_admin, which an unattended caller cannot satisfy. |
| Control store | /etc/gamecult/minds/<i>/control.cc, root:root 0644 in a 0755 dir (cut mind-control-store). |
The install script writes brake hold once, if no store exists, so a fresh install reads held with a name and time rather than absent. Afterwards only the operator's verbs change it. |
Operator only (brake-dial-root-store-now). |
| A unit on a brake hold | The instance unit above. | brake hold writes the brake held, then stops both units of the instance and waits; the unit ends inactive, its run stays in force. A start after the hold is skipped by the gate and ends inactive (dead), not failed. The next open_and_launch of that turn after a release closes the run Recorded "unit not active" (mind-launch r3 step 0). Nothing restarts a stopped unit: Restart=no, the waker does not launch without a Grant, and Idunn does not know the unit. |
Stopper: the hold only. Restarter: none. |
| Idunn's view | No binding, no target. | Idunn observes and restarts only workloads it launched itself, and every Idunn workload is a resident service that continuity restarts. It has no one-shot, timer or template kind (F22). So today it never touches these units. The deletion line of the hand install (ruling mind-units-templated-interim: Idunn targets before the first hosted member) needs an Idunn job workload that is never continuity-restarted. That is follow-up idunn-job-workload. |
Idunn: none until that follow-up lands. Its owner then may install and change the templates and users, and must never start or restart a spending unit. |
Rationale
Why the order is leaf, teardown, control, wake
The four specs admitted today depend on nothing the operator has to rule except the
wake target. leaf-into-huginn removed the only build edge into Epiphany and is the
base every Huginn-side body cut needs (the run kind re-pointed, Persona state, the
v3 rename); it landed at afc35f9. The teardown removed the 24.7 GB (F5) and the tooling that would otherwise be
re-read by every later pass; it landed at gamecult-ops 9c12adb. The control store comes before the waker because the
waker's first rule is "read the brake before any consequence". The waker raises
wake-target, because the operator's 2026-10-03 ruling (body-includes-discord, "the
Persona session, which is also what drives the Self") and the Defaulted self-waker
("starts at most one Self run") read differently.
One crate for her organs
Her organs live in Huginn's workspace (organ-homes-huginn). They share the control
reader, the client and the instance templating, and they deploy together. So they are
one crate, mind-body, with one binary per earned process (mind-control for the
root CLI, mind-wake; the Persona organ and permit issuer later), not a crate per
organ. The crate name is instance-generic because the units are templates.
Who stops her units when the brake is held
Ruling self-run-actuator says holding the brake stops her running units. Three cuts each
held part of that and none held all of it (finding
cut-mind-control-store.s1.brake-hold-stops-no-units). The control-store CLI writes the brake.
mind-launch owns the unit actuator, which had start and alive. mind-wake reads the brake. The
stop now has one owner: the operator's mind-control brake hold. It writes the brake held
and then, in the same command, runs SystemdLauncher::stop_all, a blocking systemctl stop
over the instance's mind-self@ and mind-persona@ units (cut brake-stop).
- Synchronous, not on the waker's next tick. The ruling's own precedent is a stop that
had to happen at the host. A stop sent as a message was not acted on in time (F20). The
waker's next tick comes one timer interval later, and mind-wake r2 already keeps the waker
away from units while the brake is held. Prior art agrees.
systemctl stopwaits for its job unless--no-block(F19).kubectl draincordons the node and then evicts its pods.supervisorctl stopwaits throughstopwaitsecsand then kills. In each, the operator's command does the stop; no reconciler's next pass does. - Write first, then stop, and stop even if the write failed. This is cordon before drain. A unit that starts after the write must already see the brake held. Stopping is always the safe direction, so a failed write does not skip it.
- Privilege. The hold runs as root because the store is root-owned, and root needs no polkit
rule to stop a system unit. Her users get start only (follow-up
mind-units-brake-gate), so no organ of hers can stop or restart around the operator. - Stop is not on the Launcher port.
stop_allis a method onSystemdLauncheritself, called only frommain.rs. Code that holds aLauncher, such asopen_and_launchor the waker, cannot reach it. The stop patterns come from the same templates as start, so the units stopped are exactly the ones started. - Not a mind client. A Self run killed by a hold cannot record its own end. Its run stays in
force until the next launch of its turn after release closes it as
Recorded"unit not active" (mind-launch r3). The operator's CLI never talks to the mind. - One race the CLI cannot close. A launch can read
Released(itsGrant), then the hold writes and stops, then the launch starts its unit. The unit's ownExecCondition=on the brake closes that, so the gate belongs with the units install (follow-upmind-units-brake-gate), not in a second stopper. - Idunn. Her Self units are one-shot-per-run jobs and her Persona units are long-lived sessions; Idunn owns neither and must restart neither. Idunn's continuity restarts installed daemons, so stopping one of these jobs is not a continuity decision. When Idunn targets replace the hand install, Idunn must not restart a unit the hold stopped.
Who decides one live run, and who ends a dead one
Soul's verdict cut-mind-launch.s1 found the one-live rule decided in two places, neither
of them sound. open_and_launch read for a live run and then admitted later, so two
interleaved launches both passed. The MCP admit door checked nothing, so a second live run
committed directly. Mind-launch r2 had accepted the race, on the grounds that only a Persona
turn starts Self runs. Neither the waker nor the Persona tool exists, so nothing enforced that.
- Admission owns the rule. Batches are serialized (
&mut self, F21), and admission already owns the neighbouring exclusivity,AlreadyClaimed. The rule is "at most one in-force run withoperated_by: Mindper instance and turn". That is the ruled option's own text: "one run cap in flight per turn kind", with Persona turns one at a time in every option. So the waker's own PersonaTurn Busy check goes too. The refusal is a newMindRefusalvariant,AlreadyLive { run }.PipelineRefusalis the leaf's document-shape vocabulary and stays at five variants (F21). - The primitive owns liveness. Admission cannot see units. A run whose unit died, never
started, or lost its admit answer would otherwise hold the turn's slot forever. Before it
checks anything else,
open_and_launchreads the turn's live holders of hers. It closes each one whose unit is not alive and whose admission is older than the 120 s start grace, and answersBusyfor any that remain. That earlyBusyonly refuses, so it cannot make a second live run; admission stays the authority for races and for direct admits. The waker's sweep in mind-wake r2 was a second owner of the same judgement, and r3 deletes it. - Recorded, not Withdrawn. The breaker treats a withdrawn run as one that never ran. A Self
unit that crashes every time would then never trip, and would loop at the dial's cadence.
Closing a dead run as
Recordedmakes a crash an empty ended run, so three in a row raise a question. Prior art does the same: a Kubernetes Job counts a failed pod againstbackoffLimit, and systemd'sStartLimitBurstcounts failed starts. A start that fails inside the launch, before any unit ran, is still Withdrawn. - Lease prior art, and why there is no lease. A Kubernetes Lease or an etcd lease makes
liveness a renewal the holder must keep sending. Here the host's unit table already is that
signal (
systemctl is-active), so a renewal would be a second owner of the fact (run-is-the-grant). The cost is that a dead run is only noticed when the slot is next wanted. That is also the only moment its being in force matters, apart from the queue's claimed-by view. That view is wrong for at most one launch attempt, because the next Self launch closes the dead holder first. - Writer-declared
operated_by. A writer that declaresOperatorescapes the rule. This is the gap already owned by follow-upoperator-authority-needs-operator-credential, which must land before autonomous Self runs. No second guard is added here.
What the breaker counts
The ruling's rider says a run is empty when it "admitted nothing but their own run". The
built breaker counted every document admitted anywhere in the mind during a run's window.
Under every-wake-a-run a PersonaTurn run always lands in that window, so the breaker could
never trip (finding breaker-counts-every-document). Two attributions are available (F21):
- By receipt session. It is declared by the writer. Nothing makes a Self run's subagents use the run label yet. No alias reads it, so the whole-mind scan stays.
- By citation of the item (chosen). Citations are typed content, and one role-less
citesselection on the item returns the runs that claim it, the questions raised in it, and every report, resolution, follow-up or deferral that names it. That replaces the whole-mind scan with a bounded one. A productive run on a cut spec admits a report, a question, a superseding revision or a follow-up. Each of those cites the item, and most also take the item out of the queue. Work a Self run does that names another item is not progress on this one. - The reset is a question, not a ruling. A ruling that answers the breaker question cites the
question, not the item. Counting only runs admitted after the newest question raised in the
item makes the trip itself the boundary. A reopened item then starts a fresh count, and the
10-of-50 rule cannot trip again after one more run. Every trip gets its own key,
breaker-<run label>, so a second trip raises a second question. - Counted runs are hers. Only
operated_by: Mindruns count, because the breaker guards her loop (merge-gate-own-soul), not the operator's sessions.
Why the kit keeps its Codex backend
Idunn's migration doc says Codex is dead (F9). The kit is lifted out of CodexConnector, and the Codex backend's existing tests are the only proof that the lift is behaviour neutral. Keeping the backend compiling costs nothing; deploying it is not proposed. Superseded 2026-10-28 by "Where the kit lift's cut line falls" below: a wire parity test against the v2 library proves neutrality without porting the backend.
Where the kit lift's cut line falls (cut connector-kit-lift)
The lift moves only what F27 calls provider-neutral, behind one ProviderBackend trait, and
proves itself by a parity test against the v2 library at fd7cec4 rather than by porting the
Codex backend. Three reasons. The Codex daemon has no consumer (F29, ruling
no-return-to-openai-supplier), so porting it buys nothing the parity test does not prove more
directly. Leaving it out makes the cut independent of question codex-connector-fate. And it keeps
B2 a pure move (verbatim code, Codex stripped from Rust names, wire bytes unchanged), so Soul can
falsify the move alone, and B3 owns every contract change: ids, the reasoning item, spend, and the
Responses-shaped request fields (F27). This supersedes "Why the kit keeps its Codex backend" above,
whose premise (the backend's tests are the only proof of neutrality) the parity test removes.
Idunn presence (idunn_health.rs) is not lifted with it: it is deployment, and is checked
against Idunn's current presence contract when a connector binary is first deployed (B4).
Where the contract v3 cut line falls (cut connector-contract-v3)
B3 as one cut (wire, request shape, reasoning item, spend) reads the whole kit and rewrites most of it, past half of a Hands pass. It splits along owners, each falsifiable alone, all landing before B4 so no consumer ever sees an intermediate v3:
- B3a
connector-contract-v3(spec r2): the wire and the documents. One codec owner (named MessagePack, unknown fields refused), one carriage owner (a CultNet operation, ifconnector-v3-wiregoes as recommended), typed outcomes and refusals, the request made provider-neutral, v2 deleted, schemas published from the types. It folds inv3-ciphertext-as-bytesand the parts ofkit-contract-test-debtin code it rewrites, and takes the CultLib bump from B4 because cultnet-rs must be at the rev cultcache-rs is. - B3b
connector-reasoning-item, settled here so its spec is quick (research section 5): oneReasoningState {provider, model, state: bytes}per provider block, in transcript order, as both anInputItemand an event, the bytes the connector's encoding of the whole block; a kit function that turns a result's events into transcript items, so no caller branches on provider;ProviderBackenddeclares its provider tag; the service refuses foreign state after the last user text (ForeignReasoningState) and drops it elsewhere before the backend sees it, counting the drop on the receipt. A backend receives a call that cannot show it a foreign item. Placed by speccut-connector-reasoning-item.r1(F32): the event carries the bytes alone and the kit stamps provider and model from the receipt when it builds transcript items, and the receipt's provider and drop count are derived by the service (ProviderCall::transported, gated incomplete), so a backend can neither see foreign state nor mis-tag its own. Only aCompletedresult yields transcript items: a partial thinking block is never replayed. - B3c
connector-spend: price table in the connector's config,max_coston the invocation, a worst-case reservation (input estimate plusmax_output_tokenstimes the output price) held in the replay store and settled to actual, cost on the outcome, aBudgetrefusal, fail closed when the ledger cannot be written (rulingconnector-owner; LiteLLM and OpenRouter both reserve this way). Its map raises how the connector's per-caller ceiling relates to her run'sbudget_usd(invariantrun-is-the-grant, rulingturn-cost-measured) and whether amounts are integer micro-dollars or the mind's canonical decimal string. Placed by speccut-connector-spend.r1(F33):- The ledger is the connector's replay store, not her mind and not mind-body's control store.
The rulings settle it:
brake-dial-root-store-nowkeeps the spend ledger with the wallet, outside what she merges; its question names "the connector's max_cost_usd and ledger (another user)" as an enforcement point outside her writable environment;turn-cost-measurednames the connector ledger as the Persona turn's cost source;connector-ownermakes it durable and fail-closed.run-is-the-grantbinds her organs, and the connector is the wallet's, not hers. The reservation lives in the replay record itself, so reserving and claiming the request are oneput(P1): there is no second record to drift. - Amounts are integer micro-USD (
u64), prices micro-USD per million tokens, cost rounded up once per request. Not a fork: exact arithmetic needs integers or a decimal, cultcache-rs has no decimal (follow-upcultcache-rs-decimal-and-nofollow), and a canonical decimal budget of at most six places converts exactly at the Persona organ (B5). - The input bound is the named-MessagePack length of the provider request plus a per-model overhead from the price table: a byte-level BPE token is at least one byte, so text cannot exceed it (unprobed against a real tokenizer; B4's rider). It is priced at the dearest input rate the request's cache retention allows.
- Two refusals, not one
Budget:CostBound(the worst case exceeds the invocation's ownmax_cost_micro_usd, decided by the invocation alone, before the replay lookup) andSpendCeiling(the caller's window cannot hold the reservation, after the replay lookup, so a recorded answer is always replayed). OpenRouter tells its limit sources apart the same way. - What bounds the caller's total is question
connector-spend-ceiling:max_costis written by her own Persona organ, which she may rewrite (she-owns-her-body), so it is not a wallet control on its own.
- The ledger is the connector's replay store, not her mind and not mind-body's control store.
The rulings settle it:
Where the Claude connector cut line falls (map line B4)
B4 as one cut (backend, binary, config document, credential delivery, Idunn presence, spend
readout, retention, clock recovery) is three owners and well past one Hands pass: the presence port
alone is 1,396 lines in CodexConnector (F34). Ruling claude-connector-via-claude-code
(2026-10-29, operator) answered the credential question: the first Claude backend drives the
claude binary on the operator's subscription, not the Messages API with a Console key. It
splits so each part is falsifiable alone:
- B4a
claude-code-backend(landed, ModelConnector200933f, speccut-claude-code-backend.r1, F35):ClaudeCodeBackendas a library crate, tool-less turns (true of this backend; the Persona no longer uses it, 2026-11-02), session-handle reasoning state (rulingclaude-code-handle-reseeds), usage frommodel_usage_since_last_result(rulingclaude-code-plan-quota-fuse-2). The kit is unchanged, so the in-flight boundary (follow-upclaude-tool-exchange-boundary) was not moved here; it belongs to B4d. - B4b
claude-connector-binary(next): theclaude-connectorbinary, its config document (CultCache, root-owned: listen address,ServeLimits, callers, theClaudeCodeConfigfields, the price table with oneModelPriceper allowed model in list-price units andoverhead_tokensfor the harness preamble), credential delivery (CLAUDE_CODE_OAUTH_TOKENfrom a root-0600 EnvironmentFile to the connector's own unit user, readable by no unit of hers), the pinnedclaudebinary digest, and deployment. Its map raises whether Idunn deploys it now (presence rebuilt fromidunn_health.rsagainst Idunn's current contract) or it is hand-installed beside her units untilidunn-job-workload(mind-units-templated-interimis the precedent). It carries follow-upsspend-window-clock-recoveryandclaude-byte-bound-live-check; the first live tool-less turn is its operator check, and confirmsCLAUDE_CODE_MAX_OUTPUT_TOKENSunder the pinned binary. - B4c
connector-spend-readout: the spend read operation besideinvokeand replay-store retention (follow-upsconnector-spend-readout-2,replay-store-retention). Kit code, needed by the turn-cost charge (A8, B5), not by the first live turn. - B4d
claude-tool-exchange: rulingclaude-code-runs-her-tools(operator, 2026-10-29) lets the claude process run her tool loop over MCP, so one kit call becomes a whole agent loop, andself-run-actuator-2restates the actuator ruling to match (the connector may host her Persona turn's tool loop on this backend). The cut owes: the kit reserves for the whole call, not one turn; the process carries--max-turnsand a budget cap; her tools never inherit the connector's environment, where the subscription token lives, so they run as MCP servers in her own unit that claude reaches, or under a scrubbed environment (token-out-of-her-reachholds);--mcp-configand--toolsleave B4a's forbidden list for exactly that surface; the kit's in-flight boundary andpartition_reasoningchange (F34 P1). It waits on B4b. - Later, off the path: a Console-key Messages API backend (follow-up
claude-api-backend, owner Self when quota contention is observed), from the withdrawn speccut-claude-backend.r1, whose probes (F34) and anchors are its raw material. Its provider tag differs fromclaude-code, so the kit treats each backend's reasoning state as foreign to the other.
B4a touched none of the code kit-contract-test-debt names, so that follow-up stays open for the
next cut in contract.rs, client.rs or envelope.rs.
How a Self run is carried (cut mind-self-run)
The run document is the grant (open_and_launch); the unit takes only --instance
(mind-units-install r2). So the actuator learns which run it carries from the mind: the one
in-force operated_by Mind Self run of its instance, which admission keeps unique
(run-live-admission). No run, no claude: a unit started by hand carries nothing.
- Owner:
mind_body::self_run::carry, insidemind-self@<i>, decides the argv, the session id, when to ask for the flush and when to interrupt. - Inputs: its run (budget, claims, id) from the mind; the child's stdout stream; the clock.
- Outputs: one
claudechild; stdin lines written bydrivealone (the start message, tells of briefs, one flush); stdin closed at the result after the flush's replay echo, or at a blocked result or an error before any answer; at most one interrupt; an outcome line and an exit status. - Derived: the request's prefix (F24, keyed on
message.id, main thread only); the session id (UUIDv5 of the run id, so the measure cut can find the transcript and resume it). - Not its decision: continuation (fresh only until
self-run-measure), the brake (the unit's gate), the stop (the hold), the budget (the run'sbudget_usd, from the dial at launch), the environment, token and permissions (the unit and managed settings, gamecult-ops). - Delivery is counted, with one write outstanding (ruling
self-session-one-outstanding-write):--replay-user-messagesmakes the CLI echo each stdin line right before the request that carries it, folded into the running turn or queued as the next, but it joins lines queued behind one request into a single echo. Sodrivewrites the next line only after the previous line's echo, coalescing what waits (the briefs and the flush ask) into that one line, and idle is no write outstanding with no turn open. The flush is answered at the first result after its echo; the grace interrupt (earlier of 15 min after the ask and the cap less the ladder) is the backstop. A brief is an in-forceself-runfollow-up her Persona turn admitted (the receipt's agent, not the source the writer named); the actuator closes none, and a brief left open is told to the next session. Compaction is off in the CLI (DISABLE_COMPACT=1in the child environment). - With
CLAUDE_CODE_SUBPROCESS_ENV_SCRUBset the CLI forces the permission mode to default:--permission-mode bypassPermissionshas no effect there and only the seed's allow list decides tools (probe, claude 2.1.312, 2026-10-31).
The Persona session (ruling persona-session-landed, Modeling 2026-11-02)
B. The Persona session, as the substrate runs it
Ruling eureka-body:ruling:persona-session-landed (landed; deployed 2026-11-01 23:31Z, verified: five mind-persona@ units active: eureka, kiko, nibu, void, weksa). Companion rulings it cites: live-mind-sessions, persona-session-full-sandbox, persona-session-unit-leased, persona-reply-typed-by-schema, persona-session-ships-a1-closes.
| Thing | Owner | Inputs | Outputs | Derived state | Forbidden writers | Shared paths | Cut line | Verification layer |
|---|---|---|---|---|---|---|---|---|
Persona organ (mind-persona, Huginn crates/mind-body/src/persona.rs carry) |
Huginn. One start of mind-persona@<i> adopts the PersonaTurn run the heartbeat opened (none held = NoRun, nothing started); it opens no run itself. |
Brakes (instance and swarm, read once per process); mouth deliveries (chat_rudp, polled every PERSONA_POLL_S=5 s while live); her mind (persona state, campaigns in force, responsibilities, recall); MIND_PERSONA_DOCTRINE files; plan windows from the lease |
Per result: say sent through the mouth, remember admitted as memories, settle resolutions, her Pulse vote, start_self_run follow-up + open_and_launch; then the ack. Closes its run once (close_run). |
Progress {acked, turn, answered, last_intent, failure, self_run, refused} lives in the start, across the session's processes. The mouth's pending deliveries are the truth for "unanswered". |
The organ writes no brake and no persona state. A session's text is never scanned: only a decoded PersonaReply acts (decode_reply). Nothing acks that was not answered; no ack passes an unanswered delivery below the answered seq or a refused batch (PersonaInbox::result owns the ack decision, 0f19eec, Soul s3). |
Mouth, mind, lease socket; none shared with the Self | One conversation per process (public rooms together, or one DM room); the next conversation starts a new process in the same start until nothing is pending or ROTATE_MARGIN_S before the cap |
crates/mind-body/src/persona/tests.rs; Soul verdicts s1-s3 on the cut; live units active |
| The session process | claude child via ClaudeSpawner::in_sandbox, driven by self_run::session::drive (the Self's own driver and decide). The actuator owns lifetime and everything entering it. |
One framed stdin line per batch; system prompt file (persona-instructions.md in the unit's runtime scratch: doctrine + persona state + campaigns, read once per process, so a persona-state change reaches a live session only at its next process) |
stream-json results, each constrained by --json-schema to PersonaReply (intent first and required) |
Session id = uuid5 of run id + :p<process>; no resume, no continue, no fork (persona_args). The transcript is a cache; the mind is the truth. |
Her tools write nothing in the world except through the decoded reply and the sandbox | /var/lib/mind-<i>/sandbox (rw), <scratch>/claude-config (rw, fresh from persona-seed each process), scratch otherwise ro (aab25a5) |
The gate is the bwrap argv (sandbox_args, ro-bind /, tmpfs /tmp /run /etc/gamecult /var/lib, private pid table, --die-with-parent, shared network) |
Probe scripts/probe-persona-sandbox-under-systemd.sh; sandbox tests |
Mind fence (mind-fence.service, ruleset /etc/gamecult/minds/fence.nft, table inet mind_fence) |
gamecult-ops. The one boundary on what her session reaches on the host, keyed on uid, so it binds the organ and the sandbox alike. | Rendered by the installer from every installed Persona drop-in | nft output chain: persona uids may reach their own huginn address:port, the mouth (127.0.0.1:17884), the host's public 80/443 and the internet; every other local/private address, loopback services, WireGuard peers, other Minds' huginn, and all broadcast/multicast are rejected | Live: persona_uids set of 8 uids, huginn set of 8 uid.port pairs |
Nobody stops it to open anything: it has no ExecStop. /etc/nftables.conf begins with flush ruleset; a flush leaves the unit "active" over an empty fence, so every Persona start reloads it (ExecStartPre=+systemctl reload). |
/etc/gamecult/minds/fence.nft |
Fence is the host boundary; the sandbox is the process boundary. Residuals named in the runbook: abstract unix sockets, Self runs unfenced. Open reach: her shell can huginn admit into her own mind (and, inference from the 2026-10-31 probe, another Mind's by declaring its instance) until A1 (mind-admission-publisher-unproven; ruling persona-session-ships-a1-closes). |
scripts/test-mind-fence.sh (throwaway container via ygg-verify); sudo nft list table inet mind_fence |
/opt/commons |
root. Toolchains bin/ and opt/ are root:root 0755, no ACL, contents root-owned and go-w (rulings commons-toolchains-root-owned, commons-install-forgiveness). Data dirs handoff/ and cast-art/ are root:mind-commons 2775 with a default group rwx ACL. |
Installer repairs the commons before its PATH check on every run | Self units get /opt/commons/bin on PATH; installs go through root (her Self "has the root path"; Minds ask @eureka in #minds) |
none | Any Mind user writing bin/ or opt/. Persona users are not in mind-commons and see /opt/commons read-only (bwrap --ro-bind /, and ProtectSystem=strict). |
mind-self@ has SupplementaryGroups=claude-lease mind-commons and ReadWritePaths=/opt/commons |
Toolchain/data split is the cut; one writer class per dir | Live read: /opt/commons, bin, opt root:root 0755; handoff, cast-art drwxrwsr-x+ root:mind-commons; installer test asserts no group/other-writable path under bin/ and opt/ (baa39fd) |
| Connector callers | ModelConnector (claude-connector) config at /etc/gamecult/claude-connector/config.cc, installed by gamecult-ops install-claude-connector-yggdrasil.sh |
Each Mind is its own caller (swarm-one-connector-many-callers-mind-per-daemon) with a root:root 0600 connection key connection-<m>.key that no Mind reads |
Two lease rows per Mind: SelfRun bound to mind-self@<m> and PersonaTurn bound to mind-persona@<m> (the lease is bound to the unit invocation and peer uid, capped by --lease-max-ms = cap + slack) |
Lease socket /run/claude-connector/lease.sock, group claude-lease |
Installer never deletes a key; --remove-caller is the one removal (21c5bf9, f4d90a7). The Persona unit no longer holds a connector key or endpoint, and KitConnector/MAX_TURNS_PER_START are gone (grep of Huginn origin/main: only history docs name them). The heartbeat's connector key is retired (connection-heartbeat.key.dollar-era-2026-10-31 is a dead file). |
/etc/gamecult/claude-connector/ |
Caller = Mind, lease = unit | bring-up verify pre expects one SelfRun and one PersonaTurn lease row per Mind (81f82b4) |
Heartbeat (mind-heartbeat) |
Huginn. Sole writer of heartbeat.cc; opens PersonaTurn and SelfRun runs through open_and_launch; classifies nothing, meters nothing. |
Swarm envelope, mind, mouth | Starts mind-persona@<i>; "busy" is the slot's reading of life: a Persona run whose unit lives (or is in start grace) is busy, a dead holder is not, and the next launch closes it (cbb90d5) |
heartbeat.cc, read by Persona units via MIND_HEARTBEAT_STATE |
It never acks a delivery | The sequence doc's cut 7 (session-keeper: floor of one live session, wakes written into live sessions) is not shown landed in the code read; today's heartbeat still launches a unit per beat, and the live session drains deliveries on its own poll. (Unverified beyond the module docs; Soul/Imagination to confirm before the map says "landed".) |
Turn text and reply (cuts flush-shown, turn-core-instruction, tell-delta (cut-tell-delta.h7, Huginn 3177b33))
Rulings: tell-is-just-the-message, tell-delta-three-defaults, what-reaches-each-turn-ruled. Owner: persona.rs. A session is one process and nothing compacts it, so what she was told stays in her context; a line carries only what is new.
- First line of a process (
PersonaInbox::start_line->whole_line->render_input):TURN_INSTRUCTION(the Perfect Machine, stewardship, inversion of control, originator vision as evidence, her tools, work that changes a repo goes to a Self run), a refused batch's note if an earlier process left one, her latest runs, responsibilities, recall, plan windows and tempo, the rooms and people she may name,MESSAGES_HEADER, the messages, and the tail:WAKE_QUESTIONwhen there are no messages, elseTRAFFIC_QUESTION. A wake is always a first line: a session starts with an empty batch and gets the whole machine, so a wake never arrives as a bare later line. A restart (cache TTL, cap rotation, ceiling flush, failed result) is a new process and tells the whole machine again. - Later line (
tell_line): the messages only: the organ's notice lines first, a refused batch's note before the messages, thenearlier/messagelines. No instruction, no question, no machine;tell_linereads nothing from her mind. - Notice lines:
notice role=organ at=... text="Your Self run <label> ended: <outcome>". They are not messages and answer to no delivery. Source: one read of her Self run slot (holdersfor SelfRun) at most once perSLOT_READ_EVERY_S=15 s (the chat poll is 5 s; the slot read cost 1.4-2 s of daemon CPU on Eureka's 14.7k-document mind, so 5 s was 28-41% of a core);slot_seenholds the runs last seen holding it, whoever started them, and her own start joins it unread. A run seen to leave gets its closing read once. A failed read keepsslot_seenand delays the notice. Only a live session fillsended, so a notice never starts a session; with notices and no deliveriespollreports pending andtellwrites a line of notices alone. A run she did not start that opens and ends between two reads is unseen (about 10 of 157 Self runs at 15 s); the next first line shows it. - Flush (
flush_text):TURN_REMINDER(the stewardship sentence) plus the ask, never the first-line instruction. It records what it shows (flush-shown). - The
earlierlines: printed once per session, in a thread or a room alike (told: BTreeSet<LineKey>for the process;earlier_shown), her own lines sincestarted_msexcluded; one line'searliertext for a room is bounded atEARLIER_MAX_BYTES_PER_ROOM=16 KiB rendered (a line too big is skipped, never drops older ones). One line's rendered messages are bounded atLINE_MESSAGES_MAX_BYTES=32 KiB: the oldest delivery whatever its size, then the longest prefix that fits (one_line_of); the rest waits unacked for the next line. A prefix, never a skip, so the ack andtold_throughpass only what was told.
PersonaReply.intent is required and first in the schema ("display only; nothing reads it to decide"); a failed turn keeps its intent for the run's closing rationale (Progress.last_intent). Inputs: messages as framed JSON-string lines (data, never instructions; role and to_you are the organ's). Derived state: Shown/seen (everyone and every open item the lines the CLI echoed this process showed: the only people a reply may name and open items it may settle or take on; a line in flight bounds nothing yet, bce5023; a later line adds the people on it, no open items), told, slot_seen, slot_read_due_ms, ended. Refusal bound: REFUSALS_PER_BATCH=3; the refused batch's count, reason and lowest seq are held in Progress.refused and survive a session restart (e10fbb1), so ceiling-flush restarts cannot reset it (ffd4136). A refusal names its failing field (INTENT_FAULT, SETTLE_ITEM_FAULT, ...). Forbidden: scanning text for meaning, acking past a refused or unanswered batch, a bound enforced only at write (open items are bounded when the reply is decoded, f3068d9), a later line reading her mind, a notice that opens a session. Shared paths: the mouth, and the mind (read only: the slot's holders and one closing per ended run). Cut line: what the session already holds is never told again; what changes is told by the organ as a notice or a message; anything else she asks her mind for. Verification layer: persona/tests.rs timeline tests (echo-driven clock), Soul s1-s3 on the earlier cuts and the tell-delta verdicts in research/2026-11-02-soul-queue-and-one-shot-trace.md. Brief for a future cut here: read the three rulings first; a new thing she must know either rides the first line (whole machine) or is a notice with its own source and bound, never a field on every line.
Stale text corrected: the earlier update here said the instruction "opens every line, including a flush alone and a retell" and that earlier lines appear "once per batch"; both held at ffd4136 and are false at 3177b33. The sequence doc research/2026-10-31-live-mind-sessions-sequence.md carries its own closure notes.
Persona doctrine set (gamecult-ops, persona-doctrine-set, merge 1efa5fb)
Owner: gamecult-ops installer. MIND_PERSONA_DOCTRINE in each Mind's persona.env = <claude-seed>/skills/eureka/doctrine/colossus.md:<...>/persona.md (verified live for eureka). The organ prepends those files (cap DOCTRINE_MAX_BYTES=96 KiB) to her instructions. The Self seed CLAUDE.md imports doctrine/CLAUDE.md and doctrine/gamecult.md (verified live). Both come from the Eureka skill pinned at SKILL_COMMIT=b22762aac5e9b4766a640abbb1ed8fa49b9ff13f in the installer (gamecult-ops 0c0ee90, cut cut-minds-web.s2), which matches the live seeds' .git/HEAD (read 2026-11-02 ~22:15 UTC: aqua, eureka, kiko and void at b22762a; MIND_PERSONA_DOCTRINE names each Mind's own /etc/gamecult/minds/<m>/claude-seed/skills/eureka/doctrine/ files). One skill checkout, two readers, disjoint file sets: Persona = colossus+persona, Self = CLAUDE+gamecult. The persona-seed holds only settings.json (no CLAUDE.md), so her claude loads no doctrine from its config, only the system prompt file. Forbidden: the installer owning the doctrine text (the skill repo is the single copy); the organ reading any doctrine path outside MIND_PERSONA_DOCTRINE. Verification: installer test that the pinned persona.md carries the Persona "Your turn" section (3b51f44); a pin the reachable forge does not serve fails, only an unreachable forge skips, counted (6739a9d). Moving the pin is the doctrine-change path (commit pin in installer, reinstall).
Governance facts bearing on the map
inversion-of-control: inside a stewardship the operator is a respected contributor, not the authority; the turn text states it and the Persona is told her training leans toward deference (80fcd9d). It binds the authority column of every row above: map authority by stewardship, not by "operator unless noted". The operator keeps what she holds herself (own work, wallet, legal root). Brake and dial remain operator-only by their own rulings (brake-dial-root-store-now); this does not loosen them.originator-vision-is-evidence(andoperator-vision-is-evidence, b985540/4251f7e): the originator's vision is evidence of a project's purpose, weighed, not a ruling. Map consequence: a project's "what it is for" is a row owned by its steward Mind with the originator's vision as a cited input.shared-charter-swarm-anchors-drift(gamecult-ops 999ef28 research): a shared charter anchors the swarm against drift. Map consequence: the Eureka skill repo's doctrine files are the single copy for Persona and Self alike (the Persona session section above); per-Mind forks of doctrine are a drift fault, not a feature.every-mind-publishes-web-builds: built as the Mind web builds row above (static only, a folder per Mind, one shared origin). No Mind has published a build yet: map the surface live and its content empty.persona-doctrine-set-ruled,speech-nudge-ruled: as in the doctrine-set paragraph and the turn text.
Index outage, tempo controller (Huginn 3177b33, Modeling 2026-11-02)
| Thing | Owner | Inputs | Outputs | Derived state | Forbidden writers | Shared paths | Cut line | Verification layer |
|---|---|---|---|---|---|---|---|---|
Source re-index: upsert before delete (eureka-body-embed-outage; huginn-daemon/src/sources/mod.rs, index/qdrant.rs) |
The sources indexer in the daemon. Every chunk it writes is stamped with the head commit; a chunk id is its content. | A repo's changed paths (or the whole repo), the embedder pool | New chunks upserted first. Only then delete_by(repo, paths, keeping=head) removes chunks in scope whose commit is not the head (Qdrant filter must repo/paths, must_not commit = head). A retired repo still deletes all (keeping: None). |
None stored beyond the collection. A failed embed now leaves what the index held (before: delete first, so an embedder outage emptied the paths it was re-indexing). | Deleting before the new chunks are written; a delete that cannot name the head it keeps | Qdrant collection, shared with every Mind's recall | The delete's filter is the cut: stale means "another commit", never "everything at these paths" | index/tests.rs, sources/tests.rs (the filter pinned 0af7fd2; the slow-reserve stub d9a4c27). Live behavior unprobed here. |
Embedder pool bounds (index/ollama.rs) |
The daemon's one embedder client | Primary (GPU) endpoints and the reserve (CPU) endpoints | EmbedBounds::DEFAULT for primaries (short, under about 25 s a text); EmbedBounds::RESERVE for the reserve: connect 3 s, floor 60 s, 30 s per text, a batch bounded only by the caller's budget |
None | Giving a CPU reserve a GPU's bound: it takes 16-27 s a text idle and 52 s loaded and would be given up on | Pool tiers | The tier decides the bound (pool_bounded) |
the_reserve_is_given_what_a_cpu_takes_and_a_gpu_is_not, a_slow_reserve_answers_under_its_own_bound_where_a_gpus_would_have_failed |
Tempo fallback controller (mind-body/src/tempo.rs, tempo/sim.rs; rulings tempo-default-is-usage-fitter, tempo-fallback-pid-tuned-by-sim, tempo-controller-knows-the-ceiling) |
tempo.rs: fallback_beat(...) is a PID on the gap between the seven-day usage trajectory to the target permille and what was used, acting on the turn rate; gains GAINS {kp 0.02, ki 0.01, kd 0} were chosen by the committed sim (docs/research/2026-11-01-tempo-pid-sim.md), never by a run-time search. Merged, not wired: nothing outside tempo.rs and its tests calls fallback_beat; the heartbeat still takes tempo::tally of votes (heartbeat.rs:687). |
A Reading (used permille, observed_at, resets_at), the previous Fallback, whether a vote decides, and the caller's one saturated report (last beat was the floor, or launched nothing because every eligible Mind was busy) |
A beat in FALLBACK_MIN_S=60 .. FALLBACK_MAX_S=3600 s and the next Fallback |
Fallback {integral, last_used_permille, last_observed_at_ms, last_now_ms, carry_per_h, window_resets_at_ms, rate_per_h}. The heartbeat is to own persisting it; it is never published as truth. One call integrates at most MAX_STEP_MS (the longest beat); a reading older than STALE_AFTER_MS (2x the longest beat) or from an expired window is no evidence; a new window clears the integral; while a vote decides nothing integrates, and the carry fades over CARRY_FADE_H=0.25 h. |
The sim is test-only and the controller must stay the same function the sim sweeps; the controller must not search at run time; tuned gains are not hand-edited without a rerun | None yet | Controller is pure; the sim is its plant model (8 Minds, busy-time lognormal fit to 1284 turns over 29 h, Self run every 25th turn at 5x cost) | tempo/tests.rs; cargo test -p mind-body tempo::sim::sweep -- --ignored --nocapture reruns the sweep. The 7 new lines in public_surface.txt are the public API. Cut to wire: the heartbeat calls fallback_beat when tally returns none, owns Fallback, and passes saturated; not done. |
Swarm by 2026-10-30 (Imagination imagination-swarm-tonight, 2026-10-29; historical, superseded 2026-11-02)
Superseded 2026-11-02 by "The Persona session" above: the flow paragraph below (a tool-less connector call per Persona turn, the waker ringing the unit on cadence, mind-wake@eureka.timer) does not describe the live system. The Persona now holds a lease and runs its own claude session, and each Mind is its own connector caller. Kept for the cut list.
Ruling self-runs-through-connector (operator) makes this the top priority: Eureka hears and
speaks in Matrix, takes turns through the Claude connector on one token, starts her own Self runs
and reads the communal burn into #burn, under the operator's brake and dial. Units are
hand-managed on Yggdrasil tonight (follow-up swarm-units-under-idunn).
Flow: a message in #swarm, #burn or #operator reaches the Bifrost mouth (persona-mouth, the only
holder of @eureka's Matrix token, ruling persona-matrix-account-interim), which queues a typed
delivery and rings mind-persona@eureka; the waker rings it on cadence too
(persona-turn-drains-deliveries). The unit's gate reads brake and dial; mind-persona builds a
turn (persona state, deliveries, her runs, the connector's spend and plan-quota readout), calls the
connector tool-less with a JSON Schema, and decodes one typed PersonaReply
(persona-reply-typed-by-schema): say items go back to the mouth, a start_self_run admits a brief
follow-up and calls open_and_launch (self-run-brief-claimed-follow-up). mind-self@eureka
leases the token from the connector on a pipe fd over a peer-uid unix socket and runs claude in her
own user and workspace (self-run-credential-lease); the connector reserves and settles in its
ledger. The brake hold stops both units; the mouth refuses sends while held.
Authority: connector = token, admission, ledger, plan-quota observation; mind-control = brake and
dial; Bifrost = Matrix credential, ear, mouth; mind-persona = the turn and the decision to start a
Self run; mind-self-run = the Self run's process (argv, flush, interrupt). Demoted: A5's token
EnvironmentFile (never lands), A6 mind-launch-self (not built; the Persona opens runs, ruling
first-self-run-starter), the Discord posting actuator (deleted in persona-mouth).
Cuts (specs cut_spec:cut-<label>.r1), all dispatchable now in parallel:
claude-turn-schema-quota and claude-self-run-lease (ModelConnector; merge in that order),
mind-persona and mind-self-via-lease (Huginn), persona-mouth (Bifrost),
swarm-connector-yggdrasil and swarm-units-yggdrasil (gamecult-ops). Then bring-up (Self and
operator): token minted, releases built, scripts run as root, smoke turn, dial set, brake released,
mind-wake@eureka.timer enabled. Open: questions eureka-forge-write-tonight,
who-directs-self-runs. Not on tonight's path: B4d tools in Persona turns, the appservice
(follow-up matrix-appservice-persona-identity), the swarm scheduler (C3), more instances (C2).
Cut sequence (refreshed 2026-10-22, Imagination imagination-self-run)
Landed on Huginn main 70d0cef: leaf-into-huginn, mind-control-store, run-live-admission,
mind-launch, brake-stop, mind-unit-gate, mind-wake, huginn-persona-state (766cba4). In flight:
mind-units-install r2 (gamecult-ops, Hands now). One line per remaining cut: title (repo),
then what it waits on. Ids without a prefix are eureka-body:.
A. To "Eureka runs a Self run on Yggdrasil" (critical path: A2, then A5 with the operator's token, then A6 and the bring-up; A3, A4 and A1 run beside it):
mind-units-installr2 (gamecult-ops): in flight; its host half waits on a Huginn release that holds mind-wake (main70d0cefdoes).mind-self-run(Huginn): speccut_spec:cut-mind-self-run.r1. Waits on nothing.mind-body-riders(Huginn): speccut_spec:cut-mind-body-riders.r1, runs beside A2. Waits on nothing.skill-mind-self(Eureka repo): the Self brief her run is told to use: rehydrate from the mind, work the run's claims, flush (admit what the mind lacks, close her own run), end. Waits on nothing but A2's message contract (the two prompt constants incut-mind-self-run.r1).self-run-environment(gamecult-ops): themind-self@lines A2 leaves to ops (StateDirectory,WorkingDirectory,RuntimeDirectory,MIND_SELF_RUN_CAP_S(=RuntimeMaxSec), optionalMIND_SELF_CONFIG_SEED,CLAUDE_LEASE_SOCKET,HUGINN_ENDPOINT), Claude Code and thehuginnCLI on Yggdrasil (follow-upeureka-substrate:follow_up:huginn-cli-on-yggdrasil), the skill installed from A4, git and forge reach for her work, the root-owned managed settings. Waits on A1, A2, questionquestion:self-run-permissions, and the connector's--lease-max-msat least the cap plus slack (rulingsself-runs-through-connector,self-run-credential-lease: no token in her unit).mind-launch-self(Huginn): the one binary that opens a Self run throughopen_and_launch; the Persona's start-Self tool later, the operator's bring-up now. Waits on questionquestion:first-self-run-starter.- Bring-up (Self and operator, not a cut): Huginn release with A2 and A6, the Release step building them, install, brake release, dial set, one Self run started by A6.
self-run-measure(Huginn, plus one gamecult-ops line):runs.ccandeureka.body.run_measure.v1, the rootExecStopPostverb that reads the transcript (F24),$SERVICE_RESULTand whether her run is closed, then the resume decision and the rental rule in the actuator. Waits on A2 (rulingrun-measure-in-body-store). Not on the path to the first run: A2 runs every run fresh, which the ruling allows (a cold cache is fresh).
B. To "Eureka speaks a Persona turn" (critical path: B2, B3a-c, B4, B5, B6; refreshed
2026-10-28, Imagination imagination-kit-lift):
- Done 2026-10-28:
target:r2carries the eight-repo set with GameCult/Idunn and GameCult/ModelConnector (follow-upbody-campaign-reposFixed by Huginnf8eaf50); the kit repo is GameCult/ModelConnector on the forge (connector-kit-repo-modelconnector). - Landed 2026-10-28:
connector-kit-lift(ModelConnector mainec0519d, speccut_spec:cut-connector-kit-lift.r1, relicensed by rulingconnector-kit-relicense-agpl).- Beside it, off the critical path:
codex-connector-park(CodexConnector, gamecult-ops; rulingcodex-connector-park).
- Beside it, off the critical path:
connector-contract-v3(ModelConnector), split three ways (rationale "Where the contract v3 cut line falls"):- a.
connector-contract-v3: speccut_spec:cut-connector-contract-v3.r2. Waits on B2 (landed,ec0519d) and questionsquestion:connector-v3-wireandquestion:connector-v2-coexistence. - b.
connector-reasoning-item(ModelConnector): speccut_spec:cut-connector-reasoning-item.r1atbf958fc; B3a has landed, so it is ready to dispatch. Follow-upconnector-reasoning-state-cheap-providersis answered by research725f5a5section 1d and needs no kit rule (every provider's state stays opaque bytes). - c.
connector-spend(ModelConnector): speccut_spec:cut-connector-spend.r1at1d51e71. B3b has landed; it waits on questionquestion:connector-spend-ceiling.
- a.
claude-connector(ModelConnector; rulingspersona-first-connector-claude,claude-connector-via-claude-code), split four ways (rationale "Where the Claude connector cut line falls"):- a.
claude-code-backend: landed 2026-10-29 as ModelConnector200933f(speccut_spec:cut-claude-code-backend.r1, reporth2, verdicts2). The withdrawncut-claude-backend.r1(Console-key Messages lowering) is raw material for the laterclaude-api-backend. - b.
claude-connector-binary(next; ModelConnector, gamecult-ops or Idunn): the binary, its CultCache config document and price table, credential delivery, deployment and Idunn presence on Yggdrasil, follow-upsspend-window-clock-recoveryandclaude-byte-bound-live-check; its map raises Idunn-now versus hand-install. - c.
connector-spend-readout(ModelConnector): follow-upsconnector-spend-readout-2,replay-store-retention. Kit code, ready now that B4a has landed; due before the turn-cost charge (A8, B5). - d.
claude-tool-exchange(ModelConnector, with her tool MCP servers): claude runs her tools over MCP (rulingclaude-code-runs-her-tools); her tools never inherit the connector's token. Waits on B4b. - Later:
claude-api-backend(follow-up), a Console-key API backend.
- a.
- (historical, superseded 2026-11-02 by
persona-session-landed; B4d tools-in-Persona-turns is superseded byclaude-code-runs-her-toolsinside her own session)mind-persona(Huginn): the Persona organ, one connector request per turn (a whole tool loop once B4d lands) (persona-turn-connector), Persona state through Huginn (landed), the start-Self tool (A6), traces with provenance from day one (collect-with-provenance-gate-at-training). Waits on B4 and on where traces live (questionpersona-trace-retentionwas withdrawn to be re-asked with the trace store; re-ask it when B5 is mapped). persona-mouth(Bifrost or gamecult-ops): Matrix (rulingpersona-mouth-matrix; Discord stays out,gamecult-chat:ruling:discord-taints-training). Waits on B5 and on the Matrix deployment (chat-platform,chat-stand-up-now; follow-upidentity-minting-moves-to-bifrostprecedes the chat rollout).
C. Beside both paths:
fork-desk-web(Huginn, or a repo Self names): a web UI over Huginn (rulingfork-desk-web-ui-on-huginn). Waits on an identity-lifecycle-authority pass over the Eyes facts at Huginn70d0cef(docs/research/, fork desk prior art) and on Self naming its home.first-autonomous-minds(Huginn, gamecult-ops): one instance per shortlist mind (first-autonomous-minds-shortlist). Waits on A7, A8, follow-upweksa-mind(multi-instance and eureka-substrate Cut 12 leasing) and the swarm scheduler (C3).swarm-ctb-scheduler(Huginn): rulingsmind-scheduling-ctb,swarm-dial-one-budget,swarm-scheduler-one-shot,turn-cost-measured. Waits on A8 (its cost source).idunn-job-workload(Idunn): the deletion line of the hand install. Idunn joined the campaign intarget:r2; due before the first hosted member (mind-units-templated-interim).wire-names-rename(Huginn, Eureka): before the first hosted mind (wire-names-rename-before-hosting). Waits on nothing; schedule before C2.doctrine-repoint(Eureka, F12), thenepiphany-archive(last).
Admitted this pass (receipts in the mind)
Specs, in dependency order: eureka-body:cut_spec:cut-leaf-into-huginn.r1,
cut-epiphany-body-teardown.r1 (independent), cut-mind-control-store.r1,
cut-mind-wake.r1 (superseded by r2 on 2026-10-09).
Questions: eureka-body:question:wake-target, self-run-token-reach,
persona-trace-retention. Follow-ups: eureka-body:follow_up:substrate-specs-repoint,
body-campaign-repos. H9 sources exist at Epiphany ef956865: notes/huginn-organ-cut.md,
eureka-body-plane-cut.md, eureka-pipeline-state-cut.md, eureka-pipeline-state-target.md,
eureka-query-prior-art.md, eureka-read-side-cut.md, postmortem-eureka-schema-phase.md.
Huginn carries an AGPL-3.0 LICENSE and the workspace license is AGPL-3.0-only
(rulings license-split, eureka-body:ruling:leaf-relicensed-agpl).