Huginn/docs/eureka-body-map.md

961 lines
91 KiB
Markdown

# 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.
## 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 at `afc35f9`, no build edge remains).** `grep epiphany crates/*/Cargo.toml` (Huginn 6b496eb):
`crates/huginn-mind/Cargo.toml:22` pins `epiphany-pipeline` by git at Epiphany
`ef956865`. `Cargo.lock:376-378` carries the git source. The map-r4 cut
`leaf-into-huginn` was never specified; no `cut_spec` for it exists in any
campaign (query of cut_spec headers under `eureka-substrate` and `eureka-body`).
- **F2 Leaf users in Huginn.** `pub use epiphany_pipeline;` at
`crates/huginn-mind/src/lib.rs:62`; direct `use epiphany_pipeline` in huginn-mind
`admission.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-export
`huginn_mind::epiphany_pipeline::` in eureka-state `src/lib.rs:29`, `src/main.rs:25`,
`tests/client.rs:17`, `tests/common/mod.rs:16`, `tests/mcp.rs:14,539`; huginn-daemon
`src/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`).** Epiphany `ef956865`: `epiphany-pipeline/Cargo.toml`
(lib `epiphany_pipeline`, deps anyhow, chrono, cultcache-rs at CultLib `8fc74c7`
= Huginn's own CultLib pin, rmp-serde, schemars, serde; dev serde_json),
`epiphany-pipeline/src/lib.rs` (schema-publication test reads
`CARGO_MANIFEST_DIR/../schemas/cultnet` at lib.rs:2725). `schemas/cultnet/` holds
13 `epiphany.pipeline.*.v2` schemas, `epiphany.work_organ_state.v0`,
`gamecult.persona_state.v0`, `index.json`, README; `schemas/README.md`.
- **F4 Mind deployment.** `ssh yggdrasil`: `huginn.service` active, 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 (runbook
`huginn-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`).** Units `epiphany.service`,
`epiphany-swarm.service`, `epiphany-heartbeat.service` inactive/dead;
`epiphany-model-connector.service` loaded, disabled. Users `epiphany` (988; groups
epiphany-state, bifrost-feedback-readers) and `epiphany-model` (984; group
gamecult-model); no process runs as either. Roots and sizes: `/srv/epiphany` 6.8G,
`/srv/epiphany-model-connector` 16G, `/var/lib/gamecult/epiphany` 1.9G (body.cc,
heartbeat.cc, codex-home, artifacts, capstones), `/var/lib/gamecult/epiphany-model-connector`
28K, `/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.cc` contains the string `epiphany`.
About 24.7 GB in all.
- **F6 What the live crossing still names.** `/etc/systemd/system/bifrost-persona-mouth.service`
carries flags `--epiphany-persona-mouth-trust-anchor`, `--epiphany-persona-permit-rudp`,
`--epiphany-persona-permit-trust-anchor`, `--epiphany-runtime-id epiphany-starfire`.
gamecult-ops `runbooks/bifrost-persona-feedback-yggdrasil.md:17` names
`/etc/gamecult/epiphany/bifrost-persona-feedback-anchor.msgpack`, `:45` the installed
`/usr/local/sbin/bifrost-authorize-epiphany-release` (sealed historical name), `:67`
the 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-Service` filtered 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-52` lists
targets `epiphany`, `epiphany-capstone-17`; `deploy/legacy/idunn-yggdrasil:126-135`
dispatch, `:312,:321` restart/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 of `eureka-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 with `401 token_expired`. On
Yggdrasil `codex-connector.service` is disabled, inactive since 2026-09-05 21:54 UTC,
and nothing listens on 4103. Ghostlight's codex-connector library dependency is pinned
at `68fe94b`, so a kit lift does not move Ghostlight.
- **F10 CodexConnector at origin/main 3ddc12e** (2026-09-13): `src/lib.rs` 1,958,
`daemon.rs` 1,180, `provider_backend.rs` 1,463, `idunn_health.rs` 1,396, `main.rs` 188,
`deployment/idunn/recipe.toml`. Contract types: `CodexProviderRequest` lib.rs:76-94,
`CodexInputItem` :212-229 (UserText, AssistantText, ToolCall, ToolResult; no
reasoning item), `CodexTransportInvocation` :238-245 (no `max_cost_usd`),
`CodexTransportOutcome::Completed` :449-462 (tokens, no cost), `CodexRefusal`
:464-474 (no `Budget`). Schema ids `gamecult.codex.*.v2` lib.rs:51-55.
- **F11 gamecult-ops Epiphany tooling, before the teardown** (`git ls-files | grep -i epiphany`, 7e23387):
`compose/epiphany.capstone-17.yggdrasil.yaml` 38, `compose/epiphany.yggdrasil.Dockerfile` 55,
`runbooks/epiphany-yggdrasil-deploy.md` 196, `runbooks/epiphany-starfire-bifrost-crossing.md` 101
(live crossing binding, kept), scripts `bootstrap-epiphany-yggdrasil.sh` 134,
`check-epiphany-yggdrasil.sh` 136, `construct-epiphany-image-yggdrasil.sh` 91,
`deploy-epiphany-yggdrasil.sh` 499, `deploy-epiphany-capstone-17-transaction-yggdrasil.sh` 5,
`deploy-epiphany-capstone-17-yggdrasil.sh` 79, `epiphany-capstone-17-connector-bridge-yggdrasil.sh` 50,
`provision-epiphany-provider-health-yggdrasil.sh` 116, `seal-epiphany-capstone-failure.sh` 67,
`test-deploy-epiphany-yggdrasil-order.sh` 144, `test-epiphany-container-promotion.sh` 47,
`systemd/epiphany.service` 31, `systemd/epiphany-swarm.service` 34; 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 (Epiphany `epiphany_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.r1` and
`cut-run-kind.r1` have repo `GameCult/Epiphany`, base `ef956865`;
`cut-huginn-persona-state.r2` base `bf73c42`; `cut-doctrine-persona-in-huginn.r2`
gamecult-ops base `2aeb7364`. None has a report. Since `leaf-into-huginn` they point at
a repo nothing builds from; `mind-authority` and `run-kind` have 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-retention` is 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" is `descending: 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.rs`
at `afc35f9`: `SUBJECT_LOCAL_MAX` (:898), `RESOLUTION_LOCAL_MAX` derived from it (:912),
`Sha::names_same_commit` (:272). The literal `64` at :915 (the root bound in the
fits-`Short` assert) is follow-up `eureka-substrate:follow_up:leaf-bound-hygiene`.
`leaf-key-bound` landed at Epiphany `ef956865` and came in with the leaf.
- **F17 The live Huginn release is still `107552dc`.** Read-only `ssh yggdrasil readlink
/opt/gamecult/huginn/current` on 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 `df7507b` nothing stops a unit of hers.** Read from `git show df7507b:`
on 2026-10-09 (Imagination `imagination-brake-stop`). `crates/mind-body` holds
`control.rs`, `cli.rs`, `main.rs` and `lib.rs`. `cli::run` (cli.rs:40-61) dispatches `show`,
`brake hold`, `brake release` and `dial set`. `brake hold` is `ControlWriter::open(&path)?.set_brake(false, ..)`
(cli.rs:53) and does nothing else. `git grep systemctl` over `crates/mind-body` finds nothing.
The binary sets umask 077 and calls `cli::run` with the root `CONTROL_ROOT` (main.rs:21-37).
The window probe runs the real binary with `brake release` and `brake hold` 100 times
(tests/control.rs:573-581), so a hold that ran `systemctl` would run it in the verify container.
No `launch.rs` exists yet: the Launcher is cut mind-launch. RunTurn and RunOperator carry
`NAMES` (crates/eureka-pipeline/src/lib.rs:507-508).
- **F19 Yggdrasil has no unit, user or control store of hers.** Read-only `ssh yggdrasil`
as `gamecultadmin` on 2026-10-09:
- `systemctl list-units --all 'mind-*'` and `list-unit-files 'mind-*'` both list nothing.
- `ls /etc/gamecult/minds` gives "No such file or directory". `command -v mind-control`
finds nothing. `id mind-eureka` gives "no such user".
- The host runs systemd 257 (257.13-1~deb13u1). `systemctl --help` lists
`stop 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 runbook `huginn-yggdrasil.md:86` uses
`sudo systemctl stop huginn`, and `/etc/sudoers.d/` holds `90-gamecultadmin`.
- **F20 The ruling decides when the stop happens.** Ruling `eureka-body:ruling:self-run-actuator` says:
"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 return `Held` before 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 (Imagination `imagination-launch-r3`).
- A receipt's provenance is `{ faculty, agent, session, tool }`, all declared by the writer.
No selection alias reads `session`, so attributing by session means scanning headers.
`open_and_launch` admits 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 `cites` selection with no role over every schema is
supported and tested (`crates/huginn-mind/src/query.rs:1499`). A run's header facts carry
`turn`, `operated_by` and `claims` (query.rs:139).
- The breaker at 8dee875 scans 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::admit` takes `&mut self` (`crates/huginn-mind/src/admission.rs:81`), so
admissions are serialized. The run rule is `run_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.
- `MindRefusal` is huginn-mind's (`crates/huginn-mind/src/refusal.rs`), published in
`schemas/cultnet/huginn.mind_response.v1.schema.json` and pinned by
`published_wire_schemas_match_derivation` (wire.rs:276). The leaf's `PipelineRefusal` has
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::effective` is called only by `read_effective` (control.rs:192-197).
- huginn-mind's `fixtures::run` builds an in-force SelfRun with `operated_by: Mind`
(fixtures.rs:331-342). The claim tests open several of them at once
(admission.rs:1987-2015).
- `Slug::validate_slug` and `Label::validate_label` are public
(crates/eureka-pipeline/src/lib.rs:287, 386). `RunTurn` and `RunOperator` spell their
variants through `Debug`, which is also their `NAMES` (lib.rs:436-452).
- **F22 What the mind units would land on, 2026-10-10.** Read-only `ssh yggdrasil` as
`gamecultadmin` and an Eyes pass over Idunn `6667f0f` (Imagination `imagination-mind-units`).
- Still no unit, user or store of hers: `systemctl list-unit-files 'mind-*'` lists 0;
`getent passwd` has no `mind-*` user; `/etc/gamecult/minds` is absent; no `mind-control`
on PATH. `/opt/gamecult/huginn/current` is `releases/107552dc...` and holds only
`huginn-daemon` (0755 root). The Release step builds only `-p huginn-daemon`
(gamecult-ops `runbooks/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.d` absent (`/usr/share/polkit-1/actions` exists, from systemd).
Without polkitd, systemd refuses an unprivileged caller's `StartUnit`, so no user of hers
can start a unit today. `/etc/sudoers.d` holds only `90-gamecultadmin` and 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 v257 `src/shared/dropin.c`,
`unit_file_find_dirs`: `unit_name_to_prefix`, `strrchr(prefix, '-')`,
`unit_name_build_from_type(prefix, instance, ...)`). So `User=mind-%i` works only when
the instance is the mind's slug alone.
- `SystemdLauncher::alive` counts `active`, `activating` and `reloading` as alive
(mind-launch head `origin/eureka-body/mind-launch`, launch.rs:96-99), and starts with
`systemctl start --no-block`, calling `systemctl` directly (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
transient `systemd-run` units (`Type=exec`, `Restart=no`, `DynamicUser=yes`; Idunn
`src/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.md` row 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.
- **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's `CLAUDE*`/`ANTHROPIC*` variables removed (Imagination `imagination-self-run`).
Every call ended `terminal_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: the `system/init` event, 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-id` of 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-session` starts a new random id. `--resume <unknown id>` fails before
the stream with "No conversation found with session ID: ...", exit 1, one line on stdout.
- `--resume` together with `--session-id` is 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 same `assistant`
records.
- One API request is one `message.id`. A response with several content blocks is written as
several `assistant` records with the same `message.id` and an identical `usage` (45 of 64
messages had 2 or more records; e.g. `thinking` then `tool_use`, both
`input 2 / cache_read 45175 / cache_write 39033 / output 73`). Summing records double
counts; a reader keys on `message.id`.
- `usage` fields: `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 is
`input + 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.
The `result` event carries `usage` (session totals), `modelUsage`, `total_cost_usd`,
`terminal_reason`, `num_turns`, `subagent_stats` and `queued_turn_count`.
- **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 handler `if(d.request.subtype==="interrupt")` that aborts the running
turn and snapshots the command queue; an option `cancel_queued` on interrupt; the result
schema's `queued_turn_count`. So, by source read only: user messages written while a turn
runs go to a command queue, and a control request `interrupt` ends the running turn.
Unprobed: whether a queued user message is delivered mid-turn or after the turn's `result`,
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 `fd7cec4` builds 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 with `daemon`, 11 lib tests without. `fd7cec4` is
F10's `3ddc12e` plus a README merge; F10's line numbers still hold. Files: `src/lib.rs` 1,958,
`daemon.rs` 1,180, `provider_backend.rs` 1,463, `idunn_health.rs` 1,396, `main.rs` 188, no
`vendor/`. CultLib pinned at `e171eca` (2026-09-04); 248 CultLib commits touch
`packages/cultcache-rs` or `packages/cultnet-rs` since, and the store API the connector uses
(`OwnedRedbMessagePackBackingStore`, `SingleFileMessagePackBackingStore`, `register_entry_type`,
`pull_all_backing_stores`, `get_all_with_keys`) still exists at CultLib `a4eae90`.
- **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()` and `execute(&invocation) -> CodexTransportResult` (`daemon.rs:592-653`), so one
trait is the whole seam. Codex: `provider_backend.rs` (the pinned `codex app-server` child,
`auth.json`, Responses SSE parsing), the Responses lowering `canonical_responses_body`
(`lib.rs:487-620`), `CodexDaemonConfig` (`daemon.rs:47-171`, CultCache type
`gamecult.codex.connector_config.v1`, with `codex_executable` and `codex_home`), the managed
paths (`daemon.rs:453-536`), and the Idunn recipe (npm `@openai/codex` input). `idunn_health.rs`
is Idunn runtime presence with Codex-named constants (target `codex-connector`, capability
`gamecult.codex.subscription-inference`, env `CODEX_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_serde` encodings of serde structs (`lib.rs:476-710`, client `748` and `765`,
daemon `637` and `652`); 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_json` and tool
`arguments` are JSON strings checked by `require_json_object`; they are provider-facing (JSON
Schema and the provider's own tool-call arguments), a xenos boundary. Every other JSON use is
inside `provider_backend.rs` (HTTP body, SSE, app-server JSON-RPC, `auth.json`), also xenos.
- **F29 Consumers and license** (git grep at each origin/main). Ghostlight `c43eb54` pins
`codex-connector` at `68fe94b` as a library (`Cargo.toml:18`, no `daemon` feature) and imports
the contract and client in `crates/ghostlight/src/controllers.rs:34-37` and seven other files;
Epiphany's pins are archive (target invariant `nothing-runs-from-epiphany`). gamecult-ops
`idunn/yggdrasil/bindings/codex-connector.toml.in` names origin GitHub CodexConnector; Idunn
`docs/migration.md:49-55` says retire codex-connector, and `:163` calls its recipe stale.
CodexConnector's project code is PolyForm Noncommercial 1.0.0 (`LICENSE` section 3), all 33
commits by Meta Vens; ModelConnector is AGPL-3.0-only at `1cf879f` (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 `ec0519d` plus a probe-only commit; research
`725f5a5` section 4, probes P2-P5). Baseline 77 tests green with and without `daemon`. The real
v2 frame `envelope_invocation.mp` is 735 bytes for 429 bytes of ciphertext (ratio 1.713; a
positional six-array, `0x96`) and decrypts to Ghostlight-shaped content. Ciphertext as
MessagePack `bin` makes 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 v2
`InputItem` refuses 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 the
`messagepack-base64` payload of `cultnet.operation_request.v0` round-trips through
`encode_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's `TcpFramed` frame is the kit's 4-byte big-endian frame; operation payloads may only
be `messagepack-base64` (`contracts.rs:780`); CultNet security holds one key per server where
the kit binds one per caller with ids in the AAD. Huginn's `huginn.mind` is 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 `af9fb74` on ModelConnector `bf958fc`, never pushed).
`InputItem::ReasoningState { provider, model, state }` and
`TransportEventPayload::ReasoningState { state }`, `state` under `serde_bytes` with the
envelope's `binary_schema`: all 83 unit tests and both `wire_golden_v3` tests pass with every
committed golden byte-identical; only `published_schemas` fails (the new variant, expected).
The item encodes as a one-key map whose `state` is MessagePack `bin` (`c4 03 ff 80 01` for three
bytes); a 1 MiB state encodes in 1,048,623 bytes (47 bytes of overhead); the schema shows
`state` as `format: binary`. So the receipt field `foreign_reasoning_dropped` and the new event
in the transported golden are the only wire changes B3b makes to existing fixtures. Source
reads at `bf958fc`: `ProviderBackend::execute` receives the caller's whole
`TransportInvocation` (`serve.rs:29`, via `ExecutionClaim::invocation`, `service.rs:115-119`),
and every backend writes its own `receipt.provider` (`fixtures.rs:63-83`, test backends
`serve.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 `79e14b8` on ModelConnector
`1d51e71`, never pushed; source reads at `1d51e71`). Baseline: 94 lib tests with `daemon`, 37
without, plus 4 `published_schemas`, 4 `reasoning_state` and 3 `wire_golden_v3`, all green.
P1: a `CacheBackingStore` whose `push` fails makes `CultCache::put` return the error and leaves
nothing in memory (`get` is `None`), so a reservation written with `put` before execution is
either durable or refused. `begin` already returns `ServiceError::ReplayStore` before
`live.insert` and before the backend runs (`service.rs:367-369`), so the replay store is a
fail-closed ledger by construction. P2: a second `OwnedRedbMessagePackBackingStore::new` on 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:
`CallerConfig` and `CallerAdmission` carry no spend field (`serve.rs:105-114`,
`service.rs:19-28`); `Usage` is tokens only and the receipt has no cost (`contract.rs:193-204`,
`465-479`); `TransportOutcome::Failed` carries no usage (`524-527`); replay records are never
deleted or pruned (no `delete` on the store in `service.rs`); `TransportService::open` takes 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 commits `eb021f0`, `0c9a39d` on
ModelConnector `e0c0832`, never pushed). P1: a kit lib test admitting `[UserText,
ReasoningState(other-provider), ToolCall, ToolResult, UserText]` printed `executed, 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 (research `f237623` section 4). P2: a `RawValue`
`{"b":1,"a":2}` serialized through `json!` 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)
with `serde_json` `raw_value` builds in the workspace on rust 1.95. Baseline: 138 kit lib tests
with `daemon`, green. Source reads at `e0c0832`: `ProviderBackend::execute` returns a
`TransportResult` synchronously on the serve loop's per-connection thread (`serve.rs:25-38`,
`240-262`); the backend's only result door is `ProviderCall::transported` (`service.rs:380`);
`wire_encode` is `pub(crate)` (`contract.rs:586`), so a connector crate encodes its own state
with `rmp_serde` directly; there is no connector binary, config document or Idunn presence in the
repo (`crates/` holds only `model-connector`); CodexConnector's `idunn_health.rs` is 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 report `h2`, verdict `s2` and the
crate, 2026-10-29).** `crates/claude-connector` is a library (`backend.rs`, `config.rs`,
`handle.rs`, `launch.rs`, `process.rs`, `stream.rs`; deps `model-connector` with `daemon`,
`serde_json`, `rmp-serde`, `sha2`, `zeroize`, `libc` on unix; no HTTP client). There is still no
binary, config document or Idunn presence. `model-connector` and `schemas/` are byte-identical
to `e0c0832`. Gate: 198 tests green; `cargo mutants --in-diff` 155 mutants, 121 caught, 0 missed,
2 timeouts (`process.rs` `StdChild::kill`). Live (claude 2.1.312, Starfire, haiku-5-5, four
calls): print mode emits `result.model_usage_since_last_result`, the turn's own usage; the
cumulative `modelUsage` of a resumed fork also counts the earlier turns, so it is never read;
`--system-prompt-file` replaces the system prompt as `--system-prompt` does. Argv sets
`--tools ""`, `--strict-mcp-config`, `--setting-sources ""`, `--permission-prompts none`, and
`--resume <id> --fork-session` on every resume. The child env is cleared to `PATH`, `HOME`,
`CLAUDE_CONFIG_DIR`, `CLAUDE_CODE_MAX_OUTPUT_TOKENS` and the one const
`CLAUDE_CODE_OAUTH_TOKEN`. Not exercised live: `CLAUDE_CODE_MAX_OUTPUT_TOKENS`. Open scars from
Soul `s2`: `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),
and `door-refusal-boundary` (code bills zero only for a 401 or 403 with explicitly empty usage;
ruling `claude-code-plan-quota-fuse-2`, admitted after `s2`, says 401, 403 and 429, so a 429 with
`{}` still bills the reservation until follow-up `claude-backend-hardening` lands). Follow-ups
`handle-unauthenticated`, `pin-checked-only-at-ready`.
## 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`) | `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. |
| 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. |
| 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>:<label>.service` (one-shot per Persona turn; `RuntimeMaxSec` set at install bounds a hung unit), `mind-permit@<instance>`; 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`). |
| 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`, `User=mind-%i`, `Type=exec`, no `[Install]`, so it cannot be enabled. | Inactive until started for one Persona turn. Ends by its own exit, at `RuntimeMaxSec=30min`, 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; that cut ships the binary and owns the argv. Until then a released start fails at exec (203/EXEC), and the closing launch counts it as a dead run. | 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. |
| 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 stop` waits for its job
unless `--no-block` (F19). `kubectl drain` cordons the node and then evicts its pods.
`supervisorctl stop` waits through `stopwaitsecs` and 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_all` is a method on `SystemdLauncher` itself, called
only from `main.rs`. Code that holds a `Launcher`, such as `open_and_launch` or 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` (its `Grant`), then the hold
writes and stops, then the launch starts its unit. The unit's own `ExecCondition=` on the brake closes that, so
the gate belongs with the units install (follow-up `mind-units-brake-gate`), not in a second
stopper.
- **Idunn.** Her Self and Persona units are one-shot jobs. 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
with `operated_by: Mind` per 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 new `MindRefusal` variant,
`AlreadyLive { run }`. `PipelineRefusal` is 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_launch` reads 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
answers `Busy` for any that remain. That early `Busy` only 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 `Recorded` makes 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 against
`backoffLimit`, and systemd's `StartLimitBurst` counts 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 declares `Operator` escapes the rule. This is
the gap already owned by follow-up `operator-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 `cites`
selection 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: Mind` runs 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, if
`connector-v3-wire` goes as recommended), typed outcomes and refusals, the request made
provider-neutral, v2 deleted, schemas published from the types. It folds in
`v3-ciphertext-as-bytes` and the parts of `kit-contract-test-debt` in 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): one
`ReasoningState {provider, model, state: bytes}` per provider block, in transcript order, as
both an `InputItem` and 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;
`ProviderBackend` declares 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 spec `cut-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 in `complete`), so a backend can neither see foreign state nor mis-tag its own. Only a
`Completed` result yields transcript items: a partial thinking block is never replayed.
- B3c `connector-spend`: price table in the connector's config, `max_cost` on the invocation,
a worst-case reservation (input estimate plus `max_output_tokens` times the output price) held
in the replay store and settled to actual, cost on the outcome, a `Budget` refusal, fail
closed when the ledger cannot be written (ruling `connector-owner`; LiteLLM and OpenRouter
both reserve this way). Its map raises how the connector's per-caller ceiling relates to her
run's `budget_usd` (invariant `run-is-the-grant`, ruling `turn-cost-measured`) and whether
amounts are integer micro-dollars or the mind's canonical decimal string.
Placed by spec `cut-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-now` keeps 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-measured`
names the connector ledger as the Persona turn's cost source; `connector-owner` makes it
durable and fail-closed. `run-is-the-grant` binds 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 one `put` (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-up `cultcache-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 own
`max_cost_micro_usd`, decided by the invocation alone, before the replay lookup) and
`SpendCeiling` (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_cost` is written by
her own Persona organ, which she may rewrite (`she-owns-her-body`), so it is not a wallet
control on its own.
### 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, ModelConnector `200933f`, spec `cut-claude-code-backend.r1`,
F35): `ClaudeCodeBackend` as a library crate, tool-less turns, session-handle reasoning state
(ruling `claude-code-handle-reseeds`), usage from `model_usage_since_last_result` (ruling
`claude-code-plan-quota-fuse-2`). The kit is unchanged, so the in-flight boundary (follow-up
`claude-tool-exchange-boundary`) was not moved here; it belongs to B4d.
- B4b `claude-connector-binary` (next): the `claude-connector` binary, its config document
(CultCache, root-owned: listen address, `ServeLimits`, callers, the `ClaudeCodeConfig` fields,
the price table with one `ModelPrice` per allowed model in list-price units and
`overhead_tokens` for the harness preamble), credential delivery
(`CLAUDE_CODE_OAUTH_TOKEN` from a root-0600 EnvironmentFile to the connector's own unit user,
readable by no unit of hers), the pinned `claude` binary digest, and deployment. Its map raises
whether Idunn deploys it now (presence rebuilt from `idunn_health.rs` against Idunn's current
contract) or it is hand-installed beside her units until `idunn-job-workload`
(`mind-units-templated-interim` is the precedent). It carries follow-ups
`spend-window-clock-recovery` and `claude-byte-bound-live-check`; the first live tool-less turn
is its operator check, and confirms `CLAUDE_CODE_MAX_OUTPUT_TOKENS` under the pinned binary.
- B4c `connector-spend-readout`: the spend read operation beside `invoke` and replay-store
retention (follow-ups `connector-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`: ruling `claude-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, and
`self-run-actuator-2` restates 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-turns` and 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-reach` holds);
`--mcp-config` and `--tools` leave B4a's forbidden list for exactly that surface; the kit's
in-flight boundary and `partition_reasoning` change (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 spec `cut-claude-backend.r1`, whose
probes (F34) and anchors are its raw material. Its provider tag differs from `claude-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`, inside `mind-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 `claude` child; the start message; at most one flush message; stdin closed at
the first `result`; 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's `budget_usd`, from the dial at launch),
the environment, token and permissions (the unit and managed settings, gamecult-ops).
- Robust to F25's open point: the flush is written only before the first `result`, stdin closes
at that `result`, and if the child has not exited `FLUSH_GRACE` after the flush, or the
prefix passes `CEILING_TOKENS + FLUSH_ALLOWANCE_TOKENS`, it is interrupted once. A queued
message that is never delivered costs the flush, never the stop.
### Swarm by 2026-10-30 (Imagination `imagination-swarm-tonight`, 2026-10-29)
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):
1. `mind-units-install` r2 (gamecult-ops): in flight; its host half waits on a Huginn release
that holds mind-wake (main `70d0cef` does).
2. `mind-self-run` (Huginn): spec `cut_spec:cut-mind-self-run.r1`. Waits on nothing.
3. `mind-body-riders` (Huginn): spec `cut_spec:cut-mind-body-riders.r1`, runs beside A2.
Waits on nothing.
4. `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 in
`cut-mind-self-run.r1`).
5. `self-run-environment` (gamecult-ops): the `mind-self@` lines A2 leaves to ops (`StateDirectory`, `WorkingDirectory`, `RuntimeDirectory`,
`MIND_SELF_RUN_CAP_S` (= `RuntimeMaxSec`), optional `MIND_SELF_CONFIG_SEED`, `CLAUDE_LEASE_SOCKET`, `HUGINN_ENDPOINT`), Claude Code and the `huginn` CLI on Yggdrasil (follow-up
`eureka-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, question
`question:self-run-permissions`, and the connector's `--lease-max-ms` at least the cap plus
slack (rulings `self-runs-through-connector`, `self-run-credential-lease`: no token in her unit).
6. `mind-launch-self` (Huginn): the one binary that opens a Self run through
`open_and_launch`; the Persona's start-Self tool later, the operator's bring-up now.
Waits on question `question:first-self-run-starter`.
7. 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.
8. `self-run-measure` (Huginn, plus one gamecult-ops line): `runs.cc` and
`eureka.body.run_measure.v1`, the root `ExecStopPost` verb that reads the transcript (F24),
`$SERVICE_RESULT` and whether her run is closed, then the resume decision and the rental
rule in the actuator. Waits on A2 (ruling `run-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`):
1. Done 2026-10-28: `target:r2` carries the eight-repo set with GameCult/Idunn and
GameCult/ModelConnector (follow-up `body-campaign-repos` Fixed by Huginn `f8eaf50`); the kit
repo is GameCult/ModelConnector on the forge (`connector-kit-repo-modelconnector`).
2. Landed 2026-10-28: `connector-kit-lift` (ModelConnector main `ec0519d`, spec
`cut_spec:cut-connector-kit-lift.r1`, relicensed by ruling `connector-kit-relicense-agpl`).
- Beside it, off the critical path: `codex-connector-park` (CodexConnector, gamecult-ops;
ruling `codex-connector-park`).
3. `connector-contract-v3` (ModelConnector), split three ways (rationale "Where the contract v3
cut line falls"):
- a. `connector-contract-v3`: spec `cut_spec:cut-connector-contract-v3.r2`. Waits on B2
(landed, `ec0519d`) and questions `question:connector-v3-wire` and
`question:connector-v2-coexistence`.
- b. `connector-reasoning-item` (ModelConnector): spec
`cut_spec:cut-connector-reasoning-item.r1` at `bf958fc`; B3a has landed, so it is ready to
dispatch. Follow-up `connector-reasoning-state-cheap-providers` is answered by research
`725f5a5` section 1d and needs no kit rule (every provider's state stays opaque bytes).
- c. `connector-spend` (ModelConnector): spec `cut_spec:cut-connector-spend.r1` at `1d51e71`.
B3b has landed; it waits on question `question:connector-spend-ceiling`.
4. `claude-connector` (ModelConnector; rulings `persona-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 ModelConnector `200933f` (spec
`cut_spec:cut-claude-code-backend.r1`, report `h2`, verdict `s2`). The withdrawn
`cut-claude-backend.r1` (Console-key Messages lowering) is raw material for the later
`claude-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-ups `spend-window-clock-recovery` and
`claude-byte-bound-live-check`; its map raises Idunn-now versus hand-install.
- c. `connector-spend-readout` (ModelConnector): follow-ups `connector-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 (ruling `claude-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.
5. `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 (question `persona-trace-retention` was withdrawn to be re-asked
with the trace store; re-ask it when B5 is mapped).
6. `persona-mouth` (Bifrost or gamecult-ops): Matrix (ruling `persona-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-up
`identity-minting-moves-to-bifrost` precedes the chat rollout).
**C. Beside both paths:**
1. `fork-desk-web` (Huginn, or a repo Self names): a web UI over Huginn (ruling
`fork-desk-web-ui-on-huginn`). Waits on an identity-lifecycle-authority pass over the
Eyes facts at Huginn `70d0cef` (`docs/research/`, fork desk prior art) and on Self naming
its home.
2. `first-autonomous-minds` (Huginn, gamecult-ops): one instance per shortlist mind
(`first-autonomous-minds-shortlist`). Waits on A7, A8, follow-up `weksa-mind` (multi-instance
and eureka-substrate Cut 12 leasing) and the swarm scheduler (C3).
3. `swarm-ctb-scheduler` (Huginn): rulings `mind-scheduling-ctb`, `swarm-dial-one-budget`,
`swarm-scheduler-one-shot`, `turn-cost-measured`. Waits on A8 (its cost source).
4. `idunn-job-workload` (Idunn): the deletion line of the hand install. Idunn joined the
campaign in `target:r2`; due before the first hosted member (`mind-units-templated-interim`).
5. `wire-names-rename` (Huginn, Eureka): before the first hosted mind
(`wire-names-rename-before-hosting`). Waits on nothing; schedule before C2.
6. `doctrine-repoint` (Eureka, F12), then `epiphany-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`).