Map: who stops her units when the brake is held (mind-launch r2, brake-stop r1)
Imagination imagination-brake-stop, session self-2026-10-09-eureka: body facts F18-F20, model rows for run, Brake and Units, and the rationale for a synchronous stop owned by `mind-control brake hold`. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
df7507b6aa
commit
f6b3ec5e78
1 changed files with 79 additions and 6 deletions
|
|
@ -15,6 +15,12 @@ Revised 2026-10-09 (Modeling, session `self-2026-10-09-morning`) after
|
|||
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.
|
||||
|
||||
## Pinned heads (fetched 2026-10-08 ~12:10 UTC)
|
||||
|
||||
| Repo | Ref | Commit | Note |
|
||||
|
|
@ -144,6 +150,35 @@ Each line: what was run, where, result.
|
|||
- **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.
|
||||
|
||||
## The model page (step 0b)
|
||||
|
||||
|
|
@ -154,9 +189,9 @@ pipeline kinds are in Huginn `docs/eureka-substrate-map.md`.
|
|||
| --- | --- | --- | --- |
|
||||
| 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 the opt-in `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. | Each wake opens a PersonaTurn run with no claims (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 }`. The waker withdraws a run whose unit is not alive past a 120 s grace (replaces the 24 h rule). Cadence comes from the newest PersonaTurn run's `admitted_at`, found through the `turn` and `operated_by` aliases, not `started_on` (a date cannot carry seconds). | Opening: `open_and_launch`, called by the waker for PersonaTurn and by the Persona tool for SelfRun. Ending: the Self that worked it; the waker only withdraws dead ones. Concurrency of live runs: question `eureka-body:question:mind-run-concurrency` (open). Forbidden: Hands, Soul, Imagination, Life. |
|
||||
| `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. | Each wake opens a PersonaTurn run with no claims (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 }`. The waker withdraws a run whose unit is not alive past a 120 s grace (replaces the 24 h rule). Cadence comes from the newest PersonaTurn run's `admitted_at`, found through the `turn` and `operated_by` aliases, not `started_on` (a date cannot carry seconds). | Opening: `open_and_launch`, called by the waker for PersonaTurn and by the Persona tool for SelfRun. Ending: the Self that worked it; the waker only withdraws dead ones. Concurrency of live runs: at most one in-force SelfRun of hers per instance; `open_and_launch` answers `Busy` to a second (ruling `one-live-self-run`, cut mind-launch r2). A unit stopped by a brake hold leaves its run in force until the waker's first sweep after release. Forbidden: Hands, Soul, Imagination, Life. |
|
||||
| Repetition breaker | No state of its own (rulings `self-waker`, `merge-gate-own-soul`). Trip condition derived from the item's ended SelfRun runs: last 3 empty, or 10 of the last 50. A run is empty when no non-run document citing the item has an ordinal in its window (its admission up to the next run's, or now). | Evaluated in `open_and_launch` when a Self run claims the item, not by the waker. When tripped, it admits one `question` labelled `breaker-<item local>` under the item's campaign, `raised_in` the item, and launches nothing; the existing blocked-spec recipe then removes the item from the queue. Answered by a ruling or withdrawn; either reopens the item. | `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. Holding it also stops running units (`systemctl stop`, ruling `self-run-actuator`). | Operator only (ruling `brake-dial-root-store-now`); her units read it, and the file is not writable by any user she runs as. |
|
||||
| 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. |
|
||||
| Burn-rate dial | Same store, `eureka.control.burn_rate.v1 { heat, base_cooldown_s, base_run_usd, set_at, set_by }`; `heat` in 0.05..=2.0. | Replaced whole by the CLI. Absent or out of bounds reads as heat 0, which launches nothing (fail closed, LiteLLM scar rider of ruling `connector-owner`). Schedules and her own rest arrive later as follow-up `burn-schedule-and-rest`. | Operator. Derived (never stored): wake cadence `base_cooldown_s / heat`, per-run cap `--max-budget-usd = base_run_usd × heat`, per-request `max_cost_usd`. |
|
||||
| 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 | `gamecult.model.caller_spend.v1` keyed by caller runtime id and window start, in each daemon's state root. | Rolled per window; refusal `Budget` at the cap; durable, and a missing ledger refuses the request. | Each daemon writes its own; the root caller config sets the cap. |
|
||||
|
|
@ -164,7 +199,7 @@ pipeline kinds are in Huginn `docs/eureka-substrate-map.md`.
|
|||
| 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-wake@<instance>.timer/.service`, `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. |
|
||||
| Units and instance templates | `mind-wake@<instance>.timer/.service`, `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. 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). |
|
||||
| Self-run credential | `CLAUDE_CODE_OAUTH_TOKEN` in a root-0600 EnvironmentFile per instance. | Created by `claude setup-token`; rotated by the operator. | **Open: question `self-run-token-reach`.** The Self run's `claude` process can read its own environment, so `token-out-of-her-reach` holds only if the operator names this unit (F4 of the prior-art file, follow-up `token-reach-two-units`). |
|
||||
| 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). |
|
||||
|
|
@ -191,6 +226,44 @@ one crate, `mind-body`, with one binary per earned process (`mind-control` for t
|
|||
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 waker's sweep after release withdraws it, so the operator's CLI never talks
|
||||
to the mind.
|
||||
- **One race the CLI cannot close.** A launch can read `Released`, 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.
|
||||
|
||||
### Why the kit keeps its Codex backend
|
||||
|
||||
Idunn's migration doc says Codex is dead (F9). The kit is lifted out of CodexConnector,
|
||||
|
|
@ -214,9 +287,9 @@ In dependency order, with what blocks each:
|
|||
`connector-contract-v3`, `claude-connector`, `openai-compatible-connector`: blocked
|
||||
on the kit repo joining the campaign (follow-up `body-campaign-repos`).
|
||||
3. `self-run-unit` and `skill-mind-self` (Eureka): blocked on `self-run-token-reach`
|
||||
and `wake-target`. `mind-wake` r2 and `mind-launch` r1 (Huginn) are admitted;
|
||||
`mind-launch` is blocked on question `eureka-body:question:mind-run-concurrency`
|
||||
(the Busy answer for a Self run), and `mind-wake` calls it.
|
||||
and `wake-target`. `mind-launch` r2 (Huginn `df7507b`, unblocked: the
|
||||
`one-live-self-run` ruling answered its question), then `brake-stop` r1 and
|
||||
`mind-wake` r2, which both depend on it.
|
||||
4. `mind-units-install` (gamecult-ops): after 3; needs `body-repos-on-forge`.
|
||||
5. `mind-persona` and `mouth-rebinding` (Huginn, Bifrost): blocked on `wake-target`,
|
||||
`persona-trace-retention`, the inbound-delivery cell, and the connector cuts.
|
||||
|
|
|
|||
Loading…
Reference in a new issue