The last open design question. The signal is a plain scalar in kernel-Hermes's own translation unit, written only by Hermes, read by every VM through a registered primitive. That invents nothing: it is the same registration path §XX already established when register_child_vm_ words() hands the eight STADIUM-* primitives to every VM and Hera's two sites mirror them. Reading a scalar needs no queue, channel, routing table or working arbiter, which is precisely the requirement -- the transport is the thing that failed. Names it accurately. "Semaphore" was the original framing but the mechanism is a one-way latch, and the distinction carries correctness. Per §VII.1 a VM attempts its own recovery before announcing anything, so raising the latch means recovery already failed and there is no path back to not-sinking. Single writer, many readers, one irreversible transition, both observable values valid -- which is the discipline heartbeat.c:33-34 already states verbatim and §XXVIII Stage 3 already reused once rather than inventing new locking. This is the third such variable and takes the same route for the same reason. Calling it a semaphore in code would imply counting and blocking semantics it must not acquire. Settles who reads it, which is the part §XXVIII's shared C stack constrains: a VM has no thread and cannot poll. So the check happens at the dispatch point, and the split is the design -- the kernel delivers the fact, the VM runs its own flush-and-BYE in its own context. The kernel publishes, never decides, so §XV.3 and §XIV.5 hold, and each VM running its own shutdown is what §IX.1 already ruled. The window needs no deadline because Hera's halt is physically terminal. Completes §VIII.2's two-mechanism rule with no exceptions list: floor VMs send SOS through routing, the arbiter beneath raises the latch, and which one applies is decided entirely by which side of the routing layer the sender sits on. Every design question this document opened is now ruled. What remains is build sequencing plus two small local decisions, items 17 and 18. No code is authorized. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
131 KiB
FABRIC-3.5.md — the Tripod/kernel reshuffle
Status: Standalone working document, opened 2026-09-18 by direct instruction ("we write
this in FABRIC-3.5.md as its own document, it's this important"). Topic: relocating Hermes
into the kernel, reconstituting the Tripod as Hera/Artemis/Hestia, generalizing BIRTH, and
adopting one fleet-wide failure/recovery ladder.
Why 3.5 and not 4, and why not a section of 3. FABRIC-4.md is explicitly the
lower-discipline scratchpad for theory-stage ideas "caught early... often missing a stated
'why' on purpose." This is not that: it is a ratified architectural direction with a stated
shape, and it needs the full discipline every numbered FABRIC document carries. But it is
also not bare-metal boot, which is FABRIC-3.md's declared topic, and it is not a successor
to FABRIC-3.md — FABRIC-3.md remains open, living, and authoritative for its own
topic; nothing here supersedes it, and this document does not trigger the
close-and-carry-forward discipline the FABRIC-0 → -1 → -2 → -3 chain used (that
chain triggers on closing a document). It sits at 3.5 because it is a peer in rigor and an
outsider in topic.
How to use this document. Same discipline as the rest of the series: write the decision
and its reasoning down before building, close items with a dated note citing real evidence,
never silently drop a stale claim. New findings and decisions for the reshuffle get added
here, not to FABRIC-3.md.
Scope discipline, stated up front: this is a reorganization, not an invention. Per the instruction opening it — "most of the underlying logic (Hermes routing, Artemis block I/O, Hera lifecycle/ACL, Console framebuffer) already exists and is expected to be relocated and rewired rather than rewritten." Any item in this document that turns out to need a genuinely new algorithm is by that fact out of scope and gets flagged, not quietly built.
No code is authorized by this document. Design only. Per Captain Bob's Law
(.claude/CLAUDE.md): never apply a fix not explicitly requested.
Provenance convention used below. This series distinguishes what was read from what was recalled, so each claim says which. "Traced 2026-09-18" = the file was read directly while writing this document. "Per §X" = the fact is reported by an existing FABRIC section that did its own end-to-end read; it is cited, not re-verified here. "Open" = not decided. Several items below are deliberately marked for re-verification before any code is written, because a reorganization that trusts a stale map moves the wrong things.
I. The current shape, traced rather than recalled
Before moving anything, what is actually there today. All of §I traced 2026-09-18 against the
working tree at commit e56974e, except where marked.
I.1 — Hermes is a FORTH capsule VM, and the routing layer is FORTH, not C
capsules/hermes/init.4th is short and load-bearing. Its block 5116 says it outright:
Hermes owns the one real, canonical
COMMON-CH. Every other VM subscribes into it viaVM-EXEC(see Artemis's and Hera's owninit.4th); Hera always exists first, so Hermes adds her here rather than Hera subscribing to a VM that doesn't exist yet at Hera's own birth.
It then does S" common:messaging.4th" EXEC, MSG-CD-INIT, 1 MY-CH-ID !, and adds members
1 and 0 to COMMON-CH. That is the whole of Hermes's own identity: it is the VM that
happens to hold the canonical channel.
The mechanism it holds is capsules/common/messaging.4th (504 lines) — generic per-VM
messaging vocabulary that every participating VM loads its own copy of. This matters more
than it first appears. Block 5005 allocates the arenas with CREATE ... ALLOT:
CREATE MSG-ARENA MSG-MAX MSG-CELLS * CELLS ALLOT
CREATE CH-ARENA CH-MAX CH-CELLS * CELLS ALLOT
CREATE MBR-ARENA MBR-MAX MBR-CELLS * CELLS ALLOT
CREATE/ALLOT carves out of the dictionary of whichever VM is interpreting. So today
there is no single routing fabric — there are N per-VM copies of the same vocabulary, and
Hermes is distinguished only by convention (it holds COMMON-CH; it is routing table index
1). The arbitration is distributed and conventional, not centralized and structural.
I.2 — Hermes is a hard, by-name dependency of its peers
CORRECTED 2026-09-18 by §XIII.1 — this section undercounts. It names two call sites; the full repo-wide inventory is 14 by-name FORTH references across 5 files, plus 3 C-side sites and a DoE CSV schema site. The two below are real, are among the only three live ones, and remain the sharpest edge — but read §XIII.1 for the actual extent. Left in place rather than rewritten, per this series' own "never silently drop a stale claim" rule.
Artemis does not talk to an abstract routing layer. It talks to a VM called Hermes, by name,
via VM-EXEC. Two live call sites in capsules/artemis/init.4th:
- line ~138:
S" 2 ENQUEUE-READY" S" Hermes" VM-EXEC - line ~501:
S" 2 COMMON-CH @ CH-ADD-MBR" S" Hermes" VM-EXEC
Both execute FORTH text inside Hermes's own dictionary. Either one breaks the moment Hermes is not a VM with a dictionary. This is the single most concrete consequence of the move and §III.3 is about it.
I.3 — The routing table pins 0/1/2 and says so
messaging.4th block 5006, verbatim comment:
VM routing table. §XIX: 16 slots, not 8 — see
FABRIC-3.md. Hera/Hermes/Artemis stay 0/1/2 (artemis:init.4thdepends on it); identities get NEW slots 3–10, never renumbered.
VM-NAMES-INIT registers Hera 0, Hermes 1, Artemis 2, then identities from 3 up via
DOE-IDX>MSG-IDX ( doe-idx -- msg-idx ) 2 +. "Never renumbered" is an existing, written
commitment, and the reshuffle walks straight into it (§IV.2).
I.4 — Message types in use, and the free numbers
Traced across messaging.4th: SPAWN-EVENT 1, PAUSE-EVENT 2, RESUME-EVENT 3,
KILL-EVENT 4, CONSOLE-CMD-EVENT 7, ELEVATE-REQUEST 8, BLK-ATTACH-EVENT 9. Reserved
status sentinels at the top of the range: MSG-NACKED 253, MSG-DELIVERED 255.
5 and 6 are unallocated gaps; 10 is the next in sequence. Relevant to §VII, which needs a
number for SOS. Also relevant: per §XX, SPAWN-EVENT was grepped for every consumer across
the tree and has none — "an unwired placeholder, not working infrastructure." Do not
model SOS on it as though it were a working precedent.
I.5 — BIRTH is already VM-agnostic in C; Hera's monopoly is registration-time
Per §XX, which read mama_word_birth (mama_forth_words.c:229, BIRTH's C implementation)
in full:
It is genuinely VM-agnostic — it operates on whichever
vmcalls it, with no check that the caller is Hera specifically. The only Hera-specific logic anywhere in it is the reverse guard preventing Hera from re-birthing herself.capsule_birth_baby()treatsvm->stadium_vm_idgenerically as "whoever is birthing this VM." So "any VM could theoretically birth another" is already true in the code... Hera's practical monopoly onBIRTHis a registration-time fact (onlyregister_mama_forth_words()registers it), not a check insideBIRTHitself.
This is the single most important pre-existing fact in this document. §VI is not a
redesign of BIRTH; it is a change to which VMs get it registered, plus an inheritance rule.
I.6 — Hestia already has a real bind mechanism, built and live-verified
Per §XXXII.2, closed 2026-09-16: CONSOLE-ATTACH ( name-c name-u -- ok? ) exists, is a plain
unconditional primitive (deliberately not an identity capability bit — that was tried and
rejected on two counts), and was verified end to end on amd64: UNATTENDED-BIRTH a VM named
bob → CONSOLE-ATTACH bob → S" bob" USE → typing 5 6 + . at the new console relayed to
and executed on the target identity VM, printing [zuse@bob~user] 11 ok>.
Also live: capsule_console_birth() (minimal relay proxy, bare username) paired to
capsule_runcap_birth() (the real identity, <username>~user) by name convention plus one
VM-NAME-REG executed inside the console VM's own dictionary — confirmed per-console-VM,
not a global singleton, so after-the-fact pairing already works structurally.
§V is therefore mostly a promotion of an existing, proven mechanism to an architectural role — not new construction.
I.7 — The headless-until-login invariant
Per §VIII.1 (ratified) and §XXXII.2: g_wirebind_attached_username is what
sk_console_identity_present() reads (capsule_wirebind.c:321-327), and that function is the
live "no thumbdrive, no prompt" gate. §XXXII.2 states the rule as an explicit invariant for
any new birth path: never set that global, never touch the console-pairing mechanism, or you
bring up a console with no human present and reopen a gate that was deliberately closed.
§V and §VI both have to carry this invariant forward.
I.8 — The idle pump, and why Hera is special-cased in it
Per §XX: repl.c's idle loop walks the VM registry every idle beat, VM-EXECing "MSG-TICK"
into every other live VM to drain its queue, skipping Hera — because Hera is the pump and
self-targeting VM-EXEC would hit a known reentrancy class. Hera instead gets a direct
MSG-TICK word dispatch in her own context, guarded by a fresh vm_find_word + acl_allow
check every tick rather than a cached flag.
So there is already a kernel-resident component driving the messaging layer's clock. The
pump is in repl.c, in C, today. §III is in part an admission that the arbiter's center of
gravity is already partly there.
II. What the reshuffle is, in one paragraph
Hermes stops being a peer VM on the Stadium floor and becomes a kernel-resident arbiter
between the floor and the HAL. It stops being a client of ACL-checked, Hermes-routed
messaging and becomes the routing and arbitration layer itself. The Tripod — which was Hera,
Hermes, Artemis — is reconstituted as Hera, Artemis, Hestia, with Artemis keeping its
existing storage/block-arbitration role and Hestia taking the vacated third slot. Hestia's
own role widens from owning the drawing fabric to being the bind point where users and
agent VMs (the GPIO VM of FABRIC-4.md §2, future networking VMs) attach. BIRTH becomes a
general primitive any VM with standing may invoke, with authority bounded by inheritance
rather than by a separate check. And every VM in the fleet — current and future — adopts one
escalation ladder on failure: recover gently, escalate to a from-scratch birth, then fail fast
and total.
III. Hermes moves into the kernel
III.1 — Decided (Captain Bob, 2026-09-18)
Hermes is no longer a Tripod VM. It becomes kernel-resident, sitting between the Stadium floor and the HAL. It no longer participates in Hermes-routed, ACL-checked messaging the way the other VMs do — it is the routing/arbitration layer now, not a client of it.
III.2 — What that actually changes, structurally
The self-reference in "Hermes cannot be a client of Hermes" is the whole point, and it has a
concrete reading against §I.1: today Hermes holds COMMON-CH inside a FORTH dictionary that
is itself subject to the same ACL and Stadium-heat economy as every other VM's. An arbiter
whose own arbitration state can be evicted, cooled, or ACL-denied by the mechanism it arbitrates
is a circular dependency that happens not to have bitten yet. Moving it into the kernel breaks
the circle by construction.
Note this is also what makes §VII's SOS exception (Hermes cannot route a message announcing
its own routing failure) not a special case but a direct corollary — see §VIII.
III.3 — The by-name VM-EXEC dependency has to go somewhere. Open.
SUPERSEDED 2026-09-18 by §XIV.1/§XIV.2 — this question dissolved. The three options below all assume the existing FORTH callers must be preserved. Captain Bob ruled the FORTH messaging layer legacy, so §XIII.1's inventory becomes input to a dead-FORTH cleanup rather than a migration constraint. Left in place, not rewritten.
§I.2's two Artemis call sites are the sharp edge. S" ..." S" Hermes" VM-EXEC requires a
target VM with a dictionary. Three shapes are visible; none is chosen here:
- Kernel primitives replace the by-name calls.
ENQUEUE-READYandCH-ADD-MBRbecome registered C words callable directly by any VM, and Artemis simply calls them. Closest to "relocated, not rewritten"; largest registration-surface change. - A vestigial
Hermesname that the kernel answers.VM-EXECtoHermesis intercepted and serviced by the kernel arbiter, leaving caller text untouched. Smallest diff, but it preserves a fiction and this project has a standing distaste for exactly that (§XXXII.2 Q3's rejection of an unreachable gate on doctrine grounds, not just mechanics). - Artemis subscribes through a new kernel-side registration call at birth, removing the peer-to-peer step entirely.
Decide this on paper before touching code. Whichever wins, the two Artemis lines and the
STARTUP-BANNER/LOG-INFO" scaffolding in capsules/hermes/init.4th are the concrete edit
set, plus whatever else a full grep for S" Hermes" VM-EXEC turns up — that grep has not
been run exhaustively for this document and must be, repo-wide, before scoping.
III.4 — The per-VM-arena question. Open, and the biggest unknown here.
RESOLVED 2026-09-18 by §XIV.1/§XIV.2. Answered in the "full centralization" direction, but without the migration cost this section feared: with the FORTH legacy there is no live state to port. This was named the root dependency throughout §X; it is closed. Left in place, not rewritten.
Per §I.1 the arenas are per-VM dictionary allocations. If arbitration centralizes into the
kernel, does MSG-ARENA/CH-ARENA/MBR-ARENA centralize with it, or does each VM keep its
own outbox/inbox with only the routing centralized? These are very different amounts of
work and very different risk:
- Routing-only centralization keeps
messaging.4thlargely intact, keeps the Stadium heat economy's "sender pays" property (per §XX:STADIUM-RES-PULL/-PUSHoperate on the calling VM's own reservoir), and is genuinely a reshuffle. - Full arena centralization moves FORTH dictionary state into kernel C, changes every
VM's
dict_hash, and needs the three-arch parity story re-established from scratch.
Not decided. Flagged here because the executive framing ("relocated and rewired rather than rewritten") holds comfortably for the first and is strained by the second.
III.5 — Parity consequence, stated so it is not discovered late
Any change to what register_*_words() registers, or to what init.4th loads, changes VM
dictionary contents and therefore dict_hash. §XX's own verification standard applies: the
property that matters is identical dict_hash across amd64/aarch64/riscv64, not an
unchanging absolute value. Expect the absolute hashes to move; require the cross-arch
identity to hold. Acceptance is the three-arch QEMU boot to zuse)ok> with zero
UNKNOWN WORD faults, per .claude/CLAUDE.md's non-negotiable criteria — there is no other
test.
IV. The Tripod reconstituted: Hera, Artemis, Hestia
IV.1 — Decided (Captain Bob, 2026-09-18)
Tripod = Hera, Artemis, Hestia. Artemis keeps its existing role (storage/block arbitration) unchanged. Hestia takes the slot Hermes vacates.
IV.2 — The "never renumbered" collision. Open, needs a ruling.
DISSOLVED 2026-09-18 by §XIV.1/§XIV.2 — via option 3 below, exactly as this section predicted. The FORTH routing table is legacy, so the slot-numbering question has no subject. Option 3's own caveat ("settle §III.4 first") was the correct call. Left in place, not rewritten.
§I.3's comment commits to Hera/Hermes/Artemis at 0/1/2 and to identities at 3–10 never being renumbered. Hermes leaving index 1 forces a choice, and the existing comment forecloses the laziest option:
- Hestia takes index 1. Tidy, preserves the "Tripod occupies 0/1/2" shape, and requires
no identity renumbering — but it silently redefines what slot 1 means, and
artemis:init.4th's dependence on the numbering is on 2, not 1, so it may be survivable. - Index 1 is retired; Hestia takes a fresh slot. Honest, costs a slot out of 16, and leaves a permanent hole that needs a comment explaining itself forever.
- The routing table stops being a FORTH array at all, because §III moved routing into the kernel — in which case this question dissolves into §III.4's arena question and should not be answered separately.
Not decided. Note option 3 makes 1 and 2 moot, so settle §III.4 first. This ordering matters: answering IV.2 before III.4 risks ratifying a table that the kernel move deletes.
V. Hestia's role expands: the bind point
V.1 — Decided (Captain Bob, 2026-09-18)
Beyond owning the drawing fabric, Hestia becomes the bind point for users and agent VMs — GPIO VM, future networking VMs, and whatever follows. The attach point that was previously implicit or undecided now lives here, explicitly.
V.2 — This is mostly promotion of an existing mechanism, not new construction
Per §I.6, CONSOLE-ATTACH already resolves a target's liveness by name before birthing a
console (so a typo refuses cleanly with no orphaned VM — verified live), and console/user
pairing already works after the fact. The reshuffle's contribution is declaring that this is
the fleet's attach point, and then asking the question the current mechanism does not yet
answer: it was built for human at a console attaching to an identity VM. An agent VM — a
GPIO VM per FABRIC-4.md §2, pinned and born at boot with no human anywhere near it — is a
different shape wearing the same word.
V.3 — Genuinely open
- Does an agent VM bind through
CONSOLE-ATTACHor through a sibling word?sk_repl_dispatch_line()'s pairing check reconstructs the target asconsole_get_vm_name() + "~user"(per §XXXII.2, and the cause of a real bug caught live when a console was named anything else). A GPIO VM is not a~useridentity. Either the naming convention widens or agent binding takes its own path. - Does binding an agent VM touch
g_wirebind_attached_username? It must not — §I.7. State this as an explicit invariant in whatever implements it, exactly as §XXXII.2 required of unattended birth. - Is Hestia's bind role ACL-gated, and if so where? Per
.claude/CLAUDE.md's hard rule and §XXXII.2 Q3's correction: policy belongs inACL.4th, never in C. The shape is' CONSOLE-ATTACH ACL-PIN(or a sibling word's equivalent) inACL.4th, not a C-side capability check — and specifically not avm_identity_has_cap()gate, which §XXXII.2 proved unreachable becauseidentity.installedis 0 for Hera/Hermes/Artemis and for every console-proxy VM. - Does Hestia-as-bind-point survive Hestia being a Tripod member? A Tripod VM is pinned
and born at boot (
session_register()/session_set_pinned(), perFABRIC-2.md§H.12 step 4; "pinned sessions never leave," §H.1 — cited from.claude/CLAUDE.md's and §XX's references, not re-read for this document; verify before relying on it). Whether the drawing-fabric owner and the bind-point arbiter are the same VM or two roles that happen to share a name is not settled here.
VI. BIRTH as a general primitive
VI.1 — Decided (Captain Bob, 2026-09-18)
BIRTH is a general primitive, not Hera-exclusive. Any VM with standing can invoke it. A
birthed VM inherits its ACL/DNA from the birthing VM. A VM can never birth something with
more authority than it itself holds — enforced by inheritance, not by a separate
authorization check.
VI.2 — Why this is small in C and large in policy
Per §I.5 the C implementation is already VM-agnostic; the monopoly is which registration function hands out the word. So the mechanical change is registration, and the design work is entirely in the inheritance rule.
The rule as stated is elegant precisely because it needs no gate: if a child's authority is
derived from the parent's, "cannot exceed the parent" is not a check that can be bypassed,
forgotten, or misconfigured — it is a property of how the child is constructed. This is the
same doctrine §XXXII.2 Q3 landed on from the opposite direction (a bespoke gate that can never
open is worse than no gate), and the same doctrine ZUSE-ELIGIBILITY-ADD already carries.
VI.3 — "ACL/DNA" needs a precise referent. Open, and this is the real work.
The system has a word-level ACL: four DictEntry fields (acl_ttl, acl_allow,
acl_mode, acl_pinned), a C primitive ACL-INHERIT (traced 2026-09-18 in capsules/ACL.4th's
own primitive list, line 6), and the semantics "pin is one-way; inheritance clears pin, copies
mode" (per .claude/CLAUDE.md). What §VI.1 describes is VM-level inheritance — a different
axis. Two things must be settled before any code:
- Is VM-level DNA just "the child's dictionary is built from the parent's, with per-word
state carried by the existing
ACL-INHERIT"? If yes, this is genuinely a reshuffle and the existing primitive does the work. If no, something new is being invented and that contradicts this document's own scope discipline — flag it rather than build it. - Hard constraint:
.claude/CLAUDE.mdstates "Never addacl_*fields toDictEntrybeyond the four already present." Any DNA design that wants a fifth per-word field is ruled out at the door. If VM-level authority needs state, it belongs on the VM, not on every dictionary entry.
VI.4 — "With standing" is undefined. Open.
§VI.1 says any VM with standing. Standing is not currently a concept in this codebase —
traced: no such notion appears in the ACL vocabulary or the Stadium words. Candidate readings:
(a) standing = simply having BIRTH registered, which collapses it into §VI.2's registration
question and makes the phrase decorative; (b) standing = a Stadium-economy property (sufficient
reservoir to pay for a child, consistent with "sender pays"); (c) standing = an ACL.4th
policy predicate. Not decided. (a) is the minimal reading and the one most consistent with
"do not over-engineer"; (b) is the one most consistent with the rest of the fleet's physics.
VI.5 — Invariants that must survive
- The reverse guard preventing Hera from re-birthing herself (per §I.5) still has to hold, and now has to hold for every birther with respect to itself.
capsule_birth_baby()must stay the generic path — per §XXXII.2 it is already what every(p)capsule uses across four call sites, andcapsule_runcap_birth()was explicitly established as the wrong tool for build-time-sourced births.- Unattended/agent births must not set
g_wirebind_attached_username(§I.7). ' BIRTHmust not entercapsules/ACL.4th— per.claude/CLAUDE.md,ACL.4this shared and portable,BIRTHis kernel-only, and the file deliberately omits it (comment at ~line 64). GeneralizingBIRTHdoes not change this; pin it in a kernel-specific capsule.
VII. One fleet-wide failure/recovery ladder
VII.1 — Decided (Captain Bob, 2026-09-18)
A single escalation shape, applied consistently across the fleet — current VMs and future ones (GPIO, networking, whatever comes):
- Recover gently first. For a VM with continuity to preserve (Hera being the type case), this means warm restart — resume with prior state intact.
- Escalate if gentle recovery fails. Fall back to a from-scratch
BIRTH— no continuity, rebuild fleet-state/trust from nothing. - Fail brutally once genuinely exhausted. No lingering, no partial states. Once recovery options are spent the failure is fast and total, not a slow degrade.
The value here is uniformity: one shape for Hera, Hermes, Hestia and every future VM, instead of bespoke handling per component.
VII.2 — SOS as a standard message type
Decided: SOS is a standard message type any VM can emit — not specific to any one
component. It applies to non-Tripod VMs and to two of the three Tripod VMs (Hera and Hestia).
Hermes is the exception, for a structural reason — §VIII.
Grounding and open points:
- It takes a number from §I.4's space. 5 and 6 are unallocated gaps; 10 is next in sequence. Picking a gap vs. appending is a small call but should be made deliberately, not by whoever types first.
- Do not model it on
SPAWN-EVENT. Per §XX that constant has zero consumers anywhere in the tree — it is an unwired placeholder. A message type with no dispatcher is exactly the failure modeSOScannot afford. - Open: who consumes an
SOS, and what do they do with it? The executive framing saysSOSis emitted; it does not say who acts. Given §IX (no VM assumes another's authority), the consumer set is constrained but not specified.
VII.3 — The existing precedent this should be reconciled with
WITHDRAWN 2026-09-18 by §XV.2 — this section's premise is wrong. It assumed the ladder needs a detection mechanism. Death is compudynamic: no detector, no declarer, nothing to build. The closing claim below ("a recovery ladder with no detection story only ever triggers on failures loud enough to notice by accident") does not hold. The wellness-check idea stands on its own merits, unrelated to this ladder. Left in place, not rewritten.
Per §XX, closed as captured-for-later: Captain Bob's self-healing idea — "all VMs participating in a wellness check at their nearest participating wellness center" — a distributed liveness/failure-detection concept "deliberately not built as part of this fix."
That idea and this ladder are the same problem approached from two ends: wellness checks are how a failure gets detected; the ladder is what happens after. Neither document has reconciled them. Flagged as open, and as the natural next thing to settle, because a recovery ladder with no detection story only ever triggers on failures loud enough to notice by accident.
VII.4 — Open: what "warm restart" concretely means
Warm restart implies a state boundary — what is "prior state" for a VM, and where does it live
across the restart? Candidate anchors traced or cited: the Stadium reservoir/heat state, the
VM's dictionary, its message arenas (§III.4 — if those centralize into the kernel, warm
restart gets easier, which is an argument to settle §III.4 first), its identity struct.
Not designed here. Note also that "fail brutally" must not mean "leave the Stadium's
conservation invariant K broken" — per FABRIC-3.md §XVIII that invariant is continuously
verified, so a brutal death still has to return what it held. Cited from §XVIII's heading,
not re-read for this document; verify before relying on it.
VIII. Hermes's exception: the sinking semaphore
VIII.1 — Decided (Captain Bob, 2026-09-18)
Hermes cannot emit SOS, because Hermes is the message arbiter and it cannot route a
message announcing its own routing failure. Instead of emitting SOS, Hermes raises a
semaphore signaling that it is sinking, and holds it until it goes down. This gives the rest
of the fleet a window to shut down cleanly rather than going dark with no warning.
VIII.2 — Why this is a corollary, not a special case
This is the direct consequence of §III.2. The instant Hermes stops being a client of its own routing layer, any failure-announcement it might send has no transport — the transport is the thing that failed. A semaphore is the right shape precisely because it is not a message: it does not require the failed subsystem to work, and a held-until-death signal degrades correctly (release = gone) rather than requiring a successful final transmission.
Worth stating plainly because it reframes the whole design: the fleet's failure signalling is
two mechanisms, not one, and which one a VM uses is determined by whether it is above or below
the routing layer. Everything on the Stadium floor sends SOS. The arbiter beneath it raises
a semaphore. That is a clean rule with no exceptions list to maintain.
VIII.3 — Open
FULLY SETTLED — §XVI (2026-09-18) and §XXIII (2026-09-19). The first bullet, where the semaphore lives, is answered by §XXIII: a kernel-resident single-writer scalar read through a registered primitive, and a one-way latch rather than a counting semaphore. The note below is kept as written.
PARTLY SETTLED 2026-09-18 by §XVI. The second bullet (what a clean-shutdown routine does) is answered: forced
blk_flush(0), thenBYE; Hera's is suicide via a permanentarch_halt()loop. The third (is the window bounded) answers itself — the window ends when Hera halts, a bound by construction rather than by timer (§XVI.4). The first bullet, where the semaphore mechanically lives, remains open. Left in place, not rewritten.
- Where does the semaphore live, mechanically? It must be readable by VMs whose messaging is dying. Kernel-resident, below the routing layer, is the only coherent answer, but the concrete form is unspecified.
- What does a VM's "clean shutdown routine" actually do? §IX makes this fleet-wide behavior, so it needs a single definition — and it must satisfy §VII.4's note about the conservation invariant.
- Is the window bounded? "Holds it until it goes down" gives no deadline. If Hermes sinks slowly, do VMs shut down on the signal alone or wait for release?
IX. Birther failure: no provisional handoff
IX.1 — Decided (Captain Bob, 2026-09-18)
If a VM responsible for birthing/lifecycle decisions (total Hera failure is the type case)
exhausts its own ladder — warm restart, then from-scratch BIRTH — and still cannot recover,
there is no provisional handoff of its authority to another VM. No VM assumes partial or
stolen authority on another's behalf. Instead, the rest of the fleet runs its own shutdown
routines — the same clean-shutdown behavior §VIII's sinking semaphore triggers, applied
fleet-wide.
IX.2 — Why this is consistent rather than merely austere
It is the same invariant as §VI, read from the other side. §VI says a VM can never birth something with more authority than it holds. §IX says a VM can never acquire authority it was not born with, even when the holder is gone and the authority is going begging. Together: authority only ever flows down the birth graph, never sideways and never up. No VM ends up holding authority it wasn't born with, and no VM limps in a half-failed state — every path terminates in either full recovery or fast, clean termination.
That is a strong, checkable property, and it is worth writing down as the thing to defend when some future failure mode makes a provisional handoff look temporarily attractive.
IX.3 — Open
PREMISE REJECTED 2026-09-18 by §XV.2. The first bullet asks the wrong question. Nobody declares a VM dead — it dies a compudynamic death. The "circular by construction" difficulty was an artifact of assuming death is a judgment someone renders rather than a physical outcome of the physics. Left in place, not rewritten.
- What has standing to declare a birther dead? Circular by construction: the fleet must conclude Hera is gone without Hera participating. Ties directly to §VII.3's undesigned detection story.
- Does fleet shutdown mean the kernel halts, or that the floor empties and the kernel survives? Materially different outcomes, not specified.
X. Sequencing — dependencies between the open questions
These do not decompose into independent work items; several answers foreclose others. The dependency order that fell out of writing this up:
- §III.4 (arenas: routing-only vs. full centralization) is the root. It determines whether §IV.2's routing-table question exists at all, and materially changes §VII.4's warm restart.
- §III.3 (the by-name
VM-EXECdependency) — needs a repo-wideS" Hermes" VM-EXECgrep first, which has not been done. - §IV.2 (slot numbering) — only if §III.4 leaves a FORTH routing table standing.
- §VI.3 (what "ACL/DNA" refers to) — independent of the above; gates all of §VI.
- §VII.3 (reconciling the ladder with the wellness-check idea) — gates §VII.2's consumer question and §IX.3's declare-dead question, which are the same question twice.
- §V.3 (agent-VM binding) — needs §VI settled, since an agent VM is birthed before it is bound.
XI. Punch list — design only, no code authorized
Nothing here is started. Nothing here authorizes an edit. Numbered for discussion order, not execution order.
- ✅ CLOSED 2026-09-18 (§XIII.1). Repo-wide by-name Hermes inventory: 14 FORTH sites across 5 files, 3 C-side sites, 1 DoE CSV schema site. Only 3 are live. §I.2 corrected.
- ✅ CLOSED 2026-09-18 (§XIII.4). All three citations verified against source; §V.3.4 and §VII.4 may now be relied on.
- ⬜ Settle §III.4: routing-only vs. full arena centralization. Root dependency — do first.
- ⬜ Settle §III.3 given 3.
- ⬜ Settle §IV.2 if and only if 3 leaves a FORTH routing table standing.
- ⬜ Settle §VI.3: precise referent for "ACL/DNA", against the four-field
DictEntryconstraint. - ⬜ Settle §VI.4: what "standing" means, or rule it decorative.
- ⬜ Reconcile §VII.3: the ladder vs. the wellness-check detection idea.
- ⬜ Assign
SOSa message-type number deliberately (§VII.2), with a named consumer. - ⬜ Specify the sinking semaphore's mechanism and the clean-shutdown routine (§VIII.3).
- ⬜ Answer §IX.3: what declares a birther dead, and what fleet shutdown means concretely.
- ⬜ NEW 2026-09-18 (§XIII.5). Rule on whether the Hestia Tripod leg is a new singleton, distinct from today's plural per-attach console proxies. Gates §IV.1 and §IV.2 — sits above the slot-numbering question, not beside it.
- ⬜ NEW 2026-09-18 (§XIII.2), not part of this reshuffle. Authorize a
capsules/MANIFEST.mdcorrection pass for two false claims: block 4055's "immutable ABI" (FABRIC-2.mddeclared it stale and it was never corrected) and block 2049's stated contents. Reported, not fixed, per Captain Bob's Law.
Acceptance for anything that eventually comes out of this list, per .claude/CLAUDE.md's
non-negotiable criteria: three-arch QEMU boot (amd64, aarch64, riscv64, one at a time, in
the foreground, clean before qemu), all reaching zuse)ok> with zero UNKNOWN WORD
faults, logs present under logs/, and dict_hash identical across all three architectures.
There is no other test.
XII. Explicitly not decided anywhere in this document
Collected so nothing here is mistaken for settled. Updated 2026-09-18 after §XV's rulings. Most of this list is now closed — see §XIV.2 and §XV.6 for what resolved, dissolved, or had its premise rejected.
Still genuinely open — updated again 2026-09-18 after §XVII closed item 12. Nothing structural remains; what follows are small local decisions, one placement question, and one build-time check.
Item 9— SETTLED 2026-09-19 by §XXI: actionable to the sender's birther, advisory to peers; payload descriptive, never executable; number 10.§VIII.3 first bullet— SETTLED 2026-09-19 by §XXIII: a single-writer kernel scalar in kernel-Hermes's scope, read through a registered primitive; a one-way latch, not a counting semaphore. This was the last open design question.- Item 16 (§XV.4) — an implementation check rather than a decision: every message-holding
teardown path must reach
STADIUM-EVICT, verifiable viafleet_conserved. - Item 17 (§XVI.2) — does Hera's suicide replace
BYE's current cold-restart, or become a separate word? Small, but it changes a live registered word either way. - Item 18 (§XVI.7) — if Hera is already dead, nobody performs the halt. Proposed shape (an empty floor as a kernel-observable condition) is analysis, not a ruling.
§XIII.5's proposed Hestia resolution shape remains analysis, not a ruling. §XIV.4's and §XIV.5's proposals were both superseded by §XV.4 and §XV.3 respectively.
What is decided, all by Captain Bob on 2026-09-18 and recorded in §III.1, §IV.1, §V.1,
§VI.1, §VII.1, §VII.2, §VIII.1 and §IX.1: Hermes goes into the kernel and becomes the arbiter
rather than a client; the Tripod becomes Hera/Artemis/Hestia; Hestia becomes the bind point;
BIRTH is general with authority bounded by inheritance; the fleet shares one
gentle→from-scratch→brutal ladder; SOS is a standard message type with Hermes excepted via a
sinking semaphore; and a failed birther's authority is never provisionally handed off.
XIII. Punch-list items 1 and 2 worked — CLOSED 2026-09-18, with three corrections to this document's own §I
Worked per the series' standing discipline: items 1 and 2 were the two pure-investigation
entries on §XI, carrying no design commitment, so they were executable without a ruling. All
findings below traced directly against the working tree at e56974e on 2026-09-18. No code
written, no design question answered, nothing in the tree modified.
XIII.1 — Item 1 CLOSED: the by-name Hermes inventory. §I.2 undercounted by an order of magnitude.
Correction to §I.2, which is wrong as written. It claimed the by-name dependency was Artemis's "two live call sites" and framed Artemis as the dependent. The real inventory is 13 by-name FORTH references across 5 files, plus 3 independent C-side sites. §I.2's two sites are real and still the sharpest edge, but they are not the extent of it, and the executive framing ("relocated and rewired") has to cover all of it. Full inventory:
| # | Site | Call | Live? |
|---|---|---|---|
| 1 | common/msg.4th:7 |
S" MSG-ACK-LAST" S" Hermes" VM-EXEC |
dead — §XIII.2 |
| 2 | common/msg.4th:10 |
S" MSG-NACK-LAST" S" Hermes" VM-EXEC |
dead — §XIII.2 |
| 3 | artemis/init.4th:138 |
S" 2 ENQUEUE-READY" S" Hermes" VM-EXEC |
live |
| 4 | artemis/init.4th:501 |
S" 2 COMMON-CH @ CH-ADD-MBR" S" Hermes" VM-EXEC |
live |
| 5 | process.4th:12 |
S" 1 EVENT-EMIT" S" Hermes" VM-EXEC (SPAWN) |
dead — §XIII.2 |
| 6 | process.4th:15 |
S" 2 EVENT-EMIT" S" Hermes" VM-EXEC (PAUSE) |
dead |
| 7 | process.4th:21 |
S" 3 EVENT-EMIT" S" Hermes" VM-EXEC (RESUME) |
dead |
| 8 | process.4th:26 |
S" 4 EVENT-EMIT" S" Hermes" VM-EXEC (KILL-VM) |
dead |
| 9 | doe-campaign.4th:6 |
S" Hermes" BIRTH |
not auto-run |
| 10 | doe-campaign.4th:7 |
S" LOAD-DOE" S" Hermes" VM-EXEC |
not auto-run |
| 11 | doe-campaign.4th:17 |
S" DOE-WORK" S" Hermes" VM-EXEC |
not auto-run |
| 12 | doe-campaign.4th:28 |
S" DOE-WORK" S" Hermes" VM-EXEC |
not auto-run |
| 13 | doe-campaign.4th:54 |
S" 1959 1 EXEC-DOE" S" Hermes" VM-EXEC |
not auto-run |
| 14 | messaging.4th:77 |
S" Hermes" 1 VM-NAME-REG |
live (§I.3) |
The good news is real: only 3 of the 14 are live production paths (#3, #4, #14). Seven are
dead code (§XIII.2), four are in a campaign orchestrator docs/working/architecture/ DOE-LIBRARY-HOWTO-20260819.md itself records as "Not auto-run anywhere." This materially
lowers the estimated cost of §III.3 — but it must be re-confirmed rather than trusted, since
"not auto-run" is a claim about today's boot path, not a guarantee nothing invokes it.
XIII.2 — Seven of those sites are dead code, and MANIFEST.md carries a claim FABRIC-2 already declared stale
common/msg.4th is loaded by nothing. Traced: S" common:msg.4th" EXEC appears exactly
once in the tree — inside common/msg.4th's own header comment, as usage documentation. No
capsule EXECs it. capsules/init.4th (block 2049) loads ACL.4th, block-acl.4th,
zuse-eligibility.4th, lib.4th, fabric.4th, font.4th, common:messaging.4th — and
nothing else.
This is already known and already written down. FABRIC-2.md:2770-2773 states it
outright: the HERMES-ACK/HERMES-NACK wrappers "are now obsolete (every VM has its own
local MSG-ACK-LAST/MSG-NACK-LAST — no VM-EXEC indirection needed) but the file itself
was left in place, unloaded, rather than deleted unprompted; capsules/MANIFEST.md's
'immutable ABI' claim for block 4055 is now stale." messaging.4th:411 carries the same note
from the other side: "common:msg.4th's HERMES-ACK/NACK indirection is retired."
capsules/MANIFEST.md was never corrected. Line 317 still reads: "Immutable: this is the
cross-VM ACK/NACK ABI. Every messaging VM (Hera, Hermes, Artemis) loads this at birth.
Changing the block or the word names breaks the Hermes delivery protocol." That is false on
every clause. A second MANIFEST claim also fails against the source: line 52 describes block
2049 as loading "compudynamics, VM-INIT, lib, common:msg, fleet-k, process; BIRTHs Artemis +
Hermes" — init.4th block 2049 loads none of common:msg/process and contains no BIRTH
at all (Hermes and Artemis are birthed from C — §XIII.3).
process.4th is likewise EXEC'd nowhere, making sites #5–#8 dead with it.
Reported, not fixed, per Captain Bob's Law ("if you identify a bug, report it; do not fix
it unless the user says to"). Flagged here because a reshuffle that reads MANIFEST.md as
current will conclude the ACK/NACK path is a live immutable Hermes ABI and scope around a
constraint that stopped existing in FABRIC-2.md's era. Recommend a MANIFEST.md correction
pass as its own authorized item; it is not part of this reshuffle.
XIII.3 — The Tripod is hardcoded as a name-triple in C, in three independent places
Not previously recorded in this document. Each is an edit site the moment the Tripod's membership changes (§IV.1):
capsule_birth.c:793-796—is_fleet_foundationis literallyvm_name_prefix_eq_nocase(capsule_name, "Hera") || ... "Hermes" || ... "Artemis". It gatesStadiumPatronHeadersetup and thesession_register()/session_set_pinned()pinning calls. This is the Tripod, encoded as a string test. Swapping Hermes for Hestia is a one-line change here — and that single line is what makes a VM pinned.kernel_main.c:864-874—vm_interpret(mama, "S\" Hermes\" BIRTH")plus acapsule_vm_find_by_name_nocase("Hermes", ...)liveness check and console banner. The comment citesFABRIC-2.mdD.7 (birth-by-message-only, 2026-08-28): the Tripod legs "must be alive session-less so a later thumbdrive-attach flow has a running Hermes/Artemis to message." Note the stated rationale for Hermes being born early is precisely that other flows need it available — which a kernel-resident arbiter satisfies trivially and permanently. §III is consistent with D.7's intent, not in tension with it.kernel_main.c:1007-1009—sk_vm_switch_signal_register(hermes_entry.vm_id), the preemptive context-switch registration fromFABRIC-3.md§XXVIII.
A fourth site is not code but schema, and is the expensive one: doe_log.c. The per-tick
DoE CSV hardcodes the Tripod across six columns — 16/17/18 (hera_heat_q48,
hermes_heat_q48, artemis_heat_q48, populated by doe_log_heat_by_name("Hera"/"Hermes"/ "Artemis") at lines 206-208) and 23/24/25 (switch_*_readiness), with column 22 documented
as "0=Hera, 1=Hermes, 2=Artemis by current registration order." Changing Tripod membership
changes the DoE CSV schema, which bears directly on comparability with every campaign
already run (§XXXIV/§XXXV's segmented per-ISA design in FABRIC-3.md is mid-flight). Flagged
as a real cost, not a blocker, and not something to resolve by quietly renaming a column.
XIII.4 — Item 2 CLOSED: all three flagged citations verified against source
§XI item 2 flagged three citations this document took from headings and .claude/CLAUDE.md
rather than from the source. All three check out; §V.3.4 and §VII.4 may now be relied on.
FABRIC-2.md§H.1 — real, at line 3699, "Session = Stadium patron, admission restated." The pinning claim is at line 3713: pinned sessions are exempt from "the normal departure path (heat decay /COOL): they never leave, permanently." Confirmed as §V.3.4 used it.FABRIC-2.md§H.12 — real, at line 4057, "Implementation punch list (2026-09-03)." Independently corroborated from the code side:capsule_birth.c:785-788cites "§H.12 step 4" by name for thesession_register()/session_set_pinned()soft-fail convention.FABRIC-3.md§XVIII — confirmed.Kisvm_physics_fleet_heat_sum()over all live VMs, which the reservoir-transfer accounting holds exactly atQ48_ONE, surfaced as thefleet_k_q48/fleet_conservedCSV columns. §VII.4's warning stands as written: a brutal death must still return what it held, or it breaks a continuously-verified invariant.
XIII.5 — New finding, and the most consequential of this pass: Hestia today is plural, ephemeral, and capsule-less
This was not known to §IV or §V when they were written, and it changes what §IV.1 is asking
for. Traced in capsule_console.c:
- Hestia has no capsule.
capsules/containshermes/andartemis/directories but noconsole/. A console VM's entire personality is a 3-line C string literal,CONSOLE_IDENTITY_SRC(capsule_console.c:28-31):Block 4997,S" common:messaging.4th" EXEC,MSG-CD-INIT. That is all of it. - Hestia is deliberately not on the routing table. The source comment is explicit: "No
COMMON-CHsubscription: a console's own traffic is direct 1:1 with its paired user VM (CONSOLE-CMD-EVENT), not broadcast, so there's no need to resolve an index in Hermes's own routing table for it." - Hestia is plural and per-attach.
capsule_console_birth(const char *console_name, ...)is called from three sites (capsule_wirebind.c:245,mama_forth_words.c:1591and:2015) and mints a fresh heap-built single-entry capsule directory each time. There are as many console VMs as there are attachments.
Consequence. Hera, Hermes and Artemis are singular, pinned, born-at-boot, capsule-backed, routing-table-indexed. Hestia today is none of those five things. So §IV.1's "Hestia takes the vacated third slot" is not a relocation of an existing pinned VM — as stated it would create a Hestia that does not currently exist. That is worth naming plainly, because it is the one place this document's "reorganization, not invention" scope discipline is genuinely strained.
CORRECTED 2026-09-18 by §XVII — the paragraph above compares the wrong object. The singular pinned Hestia does already exist: it is the HAL drawing fabric (
hal/console.c,framebuffer.c,vt100.c,font_8x16.c), already singular, permanent and kernel-resident — it simply has no VM face and no owner. The per-attach proxies this section measured against are what binding produces, not what Hestia is. The scope discipline is therefore not strained: the leg is an existing singleton given an owner, and the only genuinely new artifact is a capsule personality. The resolution shape proposed below was right; this justification for it was wrong. RATIFIED — see §XVII.2. Left in place, not rewritten.
The shape that resolves it without inventing anything — offered as analysis, not
ratified, Captain Bob's call: distinguish Hestia (singular, pinned, capsule-backed, the
Tripod leg — owns the drawing fabric and is the bind point, per §V.1) from console proxies
(plural, ephemeral, per-attach — exactly what capsule_console_birth() mints today,
unchanged). The Tripod leg is new; the proxies are untouched. This reading makes §V.1's "bind
point" precise: the leg is what you bind to, the proxy is what binding produces.
XIII.6 — §V and §VI are load-bearing for each other, which neither section noticed
If Hestia is the bind point (§V.1), it is Hestia that must mint console proxies — and
minting a VM is BIRTH. Today all three capsule_console_birth() call sites run in Hera's or
the caller's context. So "Hestia is the bind point" cannot be implemented while BIRTH
remains Hera-exclusive; it requires §VI.1's generalization. They are one change, not two.
Mechanically this already works, which strengthens both: two of the three call sites pass
vm->stadium_vm_id — whichever VM invoked — rather than a hardcoded Hera
(capsule_wirebind.c:245 passes mama_vm->stadium_vm_id because that path genuinely is
Hera's). That matches §I.5's finding that capsule_birth_baby() treats stadium_vm_id
generically as "whoever is birthing this VM." Parentage is already generic; only
registration is not.
This also supplies the first concrete answer to §VI.4's open "what is standing": Hestia needs
BIRTH to do its declared job, so it has standing by role. That is evidence for reading (a)
(standing = having BIRTH registered), not a ruling.
XIII.7 — Effect on §X's sequencing
Nothing found here dislodges §III.4 as the root dependency. Two adjustments:
- §III.3 is cheaper than §I.2 implied (3 live sites, not a broad web) and can be scoped as soon as §III.4 lands.
- §IV.1 needs a prior ruling that §IV.2 does not cover: is the Tripod's Hestia leg a new singleton distinct from today's proxies (§XIII.5)? That question sits above the slot numbering, not beside it. Added to the punch list as item 12.
XIII.8 — Punch-list status after this pass
- ✅ Item 1 CLOSED — inventory complete (§XIII.1), 14 FORTH sites + 3 C sites + 1 CSV schema site; §I.2 corrected.
- ✅ Item 2 CLOSED — all three citations verified against source (§XIII.4).
- ⬜ Item 12, NEW — rule on §XIII.5: is the Hestia Tripod leg a new singleton, distinct from today's per-attach proxies? Gates §IV.1 and §IV.2.
- ⬜ Item 13, NEW, not part of this reshuffle — authorize a
capsules/MANIFEST.mdcorrection pass for the two false claims in §XIII.2 (block 4055 "immutable ABI"; block 2049 contents). Reported, not fixed.
Items 3–11 unchanged and still open.
XIV. Two rulings that collapse the root dependency (Captain Bob, 2026-09-18)
XIV.1 — RULING: the existing FORTH messaging layer is legacy. Kernel Hermes is greenfield.
Captain Bob, 2026-09-18, verbatim: "as far as Hermes the VM and Hermes the kernel component, forget all the forth existing and call it legacy for now. We'll do a code cleanup for all the dead FORTH anyway."
So kernel-Hermes is not a migration of capsules/common/messaging.4th. It is a new C
implementation; the FORTH messaging layer becomes legacy on arrival and is retired by a
separate dead-FORTH cleanup pass. This reverses the framing §III.3 and §III.4 were built on —
both assumed the existing FORTH had to be carried across.
XIV.2 — What this collapses
Four of this document's open questions dissolve rather than get answered:
- §III.4 (the root dependency) — RESOLVED by fiat, in the "full centralization" direction,
but without the migration cost that made it frightening. The kernel owns message and
channel state;
messaging.4th's per-VMCREATE/ALLOTarenas are legacy, not something to centralize. §III.4 feared a painful port of live FORTH state; there is no port. - §III.3 (the by-name
VM-EXECdependency) — dissolved. The three-option framing (kernel primitives / vestigial name / birth-time registration) was about preserving callers. With the FORTH legacy, §XIII.1's 14-site inventory stops being a migration constraint and becomes an input to the cleanup pass. §XIII.1's inventory is still the right list — its purpose changed, not its content. - §IV.2 (routing-table slot numbering) — dissolved, exactly as §IV.2's own option 3
predicted: "the routing table stops being a FORTH array at all... in which case this
question dissolves into §III.4's arena question." It did.
VM-NAMES-INIT's 0/1/2 pinning and the "never renumbered" commitment are legacy artifacts, not constraints on the new design. - §XI item 13 (the
MANIFEST.mdcorrections) — absorbed. Bob's "we'll do a code cleanup for all the dead FORTH anyway" covers §XIII.2's findings. Still worth doing deliberately; no longer a separate ask.
§XIII.1, §XIII.2 and §XIII.3 keep their value — the C-side sites (§XIII.3) are not legacy and remain real edit sites, and the dead-FORTH inventory now feeds the cleanup.
XIV.3 — RULING: Artemis is the persistence path. And it needs no layering inversion.
Captain Bob, 2026-09-18: "For persistence though, use Artemis as much as possible. We're good with that part for sure." Ratified.
A layering question this raises, traced 2026-09-18 and answered cleanly. Artemis is a VM on the Stadium floor; kernel-Hermes sits below the floor. "Persist via Artemis" reads at first like an inversion — the kernel arbiter calling up into a floor VM. It is not, because storage is already two layers, not one:
- The block subsystem is kernel/shared C, below the floor:
src/block_subsystem.c,src/blkio_*.c,src/starkernel/virtio/virtio_blk.c.capsule_loader.c:98-100callsblk_subsys_init()andblk_subsys_add_raw_device()directly, with no Artemis involved at all. - Artemis is the storage policy arbiter on the floor: it owns attach/registration
(
blk_subsys_attach_device()via theBLK-ATTACHprimitive —repl.c:525-531states it outright, "Artemis's own domain now, not Hera's") and the on-disk Artemis format.
So the ruling is satisfiable without inversion, by keeping the two straight: floor-level and fleet persistence goes through Artemis, its domain and its format; anything kernel-Hermes itself needs uses the same block subsystem Artemis is built on — never a new one, and never by messaging Artemis.
Worth recording that the kernel already calls up into Artemis by name today
(repl.c:539-541, S" %llu HERA-BLK-ATTACH-REQ" S" Artemis" VM-EXEC), with a comment
explaining it is a direct VM-EXEC rather than a real message because Hera can't use her own
MSG-SEND there. That precedent exists; this section is about not depending on it.
XIV.4 — The one real hazard in the persistence ruling, and the shape that avoids it
Kernel-Hermes must not depend on Artemis for the persistence it needs during its own failure. This is the same structural trap as §VIII: if Hermes must write something at death and reaching Artemis requires routing, the dependency closes a circle on exactly the mechanism that is broken. §VIII solved the signalling case with a semaphore; the persistence case needs the same discipline rather than a second special case.
Recommended shape — analysis, not a ruling, Captain Bob's call: kernel-Hermes holds only
reconstructible state. If the arbiter's routing state can be rebuilt from the live VM
registry (which capsule_birth.c already maintains as the authority on who exists), then:
- Hermes has nothing that must be persisted, so the Artemis dependency never arises.
- §VII.4's "what does warm restart mean for Hermes" becomes trivial — rebuild from the registry, no saved state to reconcile.
- Losing in-flight messages when the arbiter dies is consistent with §VII.1 step 3's "no lingering, no partial states," and with §VIII's clean-shutdown window.
This costs message durability across an arbiter death. Flagged as the real trade, and the one to rule on: if in-flight messages must survive Hermes dying, Hermes needs durable state, and that durable state cannot route through Artemis at death-time.
XIV.5 — Still open, and now more urgent, not less: the scheduler firewall
Raised 2026-09-18 and not yet ruled on. Recorded here because §XIV.1 changes its timing.
The fleet already has a scheduler: §XXVIII built timer-driven preemption, and MSG-SEND
(messaging.4th block 5021) already calls SWITCH-MARK-WORK → sk_vm_switch_signal_mark_work(),
setting has_work on the target's switch slot; sk_vm_switch_signal_tick() switches when
readiness >= SK_SWITCH_READINESS_THRESHOLD and has_work. Message arrival is already
a scheduling input. This directly contradicts VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md's
"Explicitly not wanted: a VM scheduler. Nothing gets built that decides whose turn it is to
execute" — a live tension in the project that predates this document and is not this
document's to resolve, but is this document's to not make worse.
Why §XIV.1 sharpens it. Today that coupling runs FORTH MSG-SEND → C primitive → switch
slot. With the FORTH legacy, kernel-Hermes becomes the caller of
sk_vm_switch_signal_mark_work() directly — eligibility marking moves inside the arbiter.
That is the consolidation to guard against, and it happens by default unless the boundary is
stated up front. Greenfield is the right time to state it; retrofitting it later is how this
becomes a conventional scheduler by accident.
Proposed invariant, for ruling: kernel-Hermes carries, resolves and ACL-checks. It
publishes facts — "this VM has mail" — and never reads readiness, never orders traffic by
priority, and never decides turn order. switch.c remains the sole owner of "who runs next."
The argument for it is empirical and from this project's own history: §XXVIII.2 is a record of
switch-storms caused by two sources of truth disagreeing about who was running
(vm_log_attributed_vm() vs. the real trampoline state) — QEMU pinned near 100%, serial log
frozen solid. This codebase punishes duplicated authority with hangs. Two deciders would be
worse than one scheduler.
XIV.6 — Punch list after these rulings
- ✅ Item 3 (§III.4, the root) — RESOLVED by §XIV.1. Kernel owns messaging; FORTH is legacy.
- ✅ Item 4 (§III.3) — DISSOLVED by §XIV.1; §XIII.1's inventory becomes cleanup input.
- ✅ Item 5 (§IV.2) — DISSOLVED by §XIV.1, as §IV.2 option 3 predicted.
- ✅ Item 13 — ABSORBED into the dead-FORTH cleanup pass.
- ⬜ Item 14, NEW — rule on §XIV.5's scheduler firewall invariant. Raised, not ruled.
- ⬜ Item 15, NEW — rule on §XIV.4: does message durability survive an arbiter death? If yes, Hermes needs durable state that cannot route through Artemis at death-time.
- ⬜ Items 6, 7, 8, 9, 10, 11, 12 unchanged and still open.
Note the shape of what remains. With the root dependency resolved, the surviving open items are no longer about mechanism — they are about authority: who may birth (6, 7), who decides a VM is dead (8, 11), who owns turn order (14), and what survives a death (15). That is a better class of question to be left with, and §IX's "authority only ever flows down the birth graph" is the principle most of them should be tested against.
XV. The authority questions, answered (Captain Bob, 2026-09-18)
§XIV.6 observed that the surviving open items were no longer about mechanism but about authority — who may birth, who declares a VM dead, who owns turn order, what survives a death. All four are answered here, and three of the four are answered by rejecting the question's premise rather than by naming an owner. That pattern is the finding.
XV.1 — RULING: any VM may birth a near-clone of itself, carrying its own ACL DNA
Captain Bob, verbatim: "Any VM can birth another VM that is almost a clone of itself with its own ACL DNA whatever ya wanna call it."
This closes §VI.3 and §VI.4 together:
- §VI.3 ("ACL/DNA" needs a precise referent) — ANSWERED: inheritance by copy, from the
parent. The child is almost a clone of its birther: it starts from the parent's own
state and carries its own DNA — a copy it owns and may diverge, not a shared reference
to the parent's. This is the reading §VI.3 hoped for: it uses the existing word-level
ACL-INHERITsemantics (pin cleared, mode copied) rather than inventing a VM-level construct, so it stays inside the "reorganization, not invention" scope, and it does not need a fifthacl_*field onDictEntry(the constraint §VI.3 flagged). - §VI.4 ("standing" is undefined) — ANSWERED: standing is decorative. "Any VM can birth"
confirms reading (a) — standing means nothing more than having
BIRTHregistered. Per §I.5 the C implementation is already VM-agnostic, so this is a registration change, exactly as §VI.2 predicted. No Stadium-economy gate, noACL.4thpredicate. Consistent with.claude/CLAUDE.md's "do not over-engineer — if the user says 'BIRTH is a primitive', that is the complete specification."
Why the authority bound still holds without a check. A near-clone cannot exceed its parent because it is built from the parent. §VI.1's "enforced by inheritance, not by a separate authorization check" is therefore literal, not aspirational — there is no gate to bypass because there is no gate.
XV.2 — RULING: nobody declares a VM dead. It dies a compudynamic death.
Captain Bob, verbatim: "Nobody declares a VM dead, it died a compudynamic death."
This rejects the premise of §IX.3 and of §VII.3's detection gap, and both were wrong to ask what they asked. §IX.3 asked "what has standing to declare a birther dead?" and called it "circular by construction." The circularity was an artifact of assuming death is a judgment someone renders. It is not: death is a physical outcome of the compudynamics — heat exhausts, the reservoir empties, the patron leaves the Stadium floor. There is no detector, no quorum, no supervisor, and nothing to build.
This retires the largest scheduler-shaped risk in the whole design. §VII's ladder appeared to need a supervisor — some component watching liveness and deciding to escalate — and §XIV.5 warned that a supervisor with a timer is a scheduler's twin. With death compudynamic, that component does not exist and is not needed. The ladder is self-applied while a VM is alive; once it is dead it is dead, which is exactly §VII.1 step 3's "fast and total, not a slow degrade." No actor, no handoff (§IX.1 already forbade one), no partial states.
Consequence for §VII.3. The "wellness check at the nearest participating wellness center"
idea recorded in FABRIC-3.md §XX does not need to be reconciled with this ladder as a
detection mechanism, because no detection mechanism is required. It remains an independent idea
on its own merits. §VII.3's framing ("a ladder with no detection story only fires on failures
loud enough to notice by accident") was built on the same wrong premise and is withdrawn.
XV.3 — RULING: nobody owns turn order. Turn order is compudynamic.
Captain Bob, verbatim: "Nobody owns the turn order. The turn order is compudynamic."
This answers §XIV.5 (item 14) with a stronger invariant than the one proposed there. §XIV.5
suggested a firewall — "switch.c remains the sole owner of who runs next." That named an
owner, and naming an owner is what a conventional scheduler is. The correct invariant names
none:
Turn order is an emergent output of the compudynamics, not a decision any component makes. Kernel-Hermes publishes facts into that system — "this VM has mail" — and computes no ordering. Neither does anything else.
This is consistent with the project's own standing position, independently of this document:
FABRIC-3.md §XIX records the design direction as "fix the gap without a scheduler," with
the reasoning that "a fixed round-robin across identities would just be a different hardcoded
policy — not in the spirit of" the design. §XV.3 is that same position restated for the
reshuffle.
One honest observation, offered as a flag rather than an objection. Traced 2026-09-18:
sk_vm_switch_signal_tick() gates on readiness >= SK_SWITCH_READINESS_THRESHOLD && has_work,
and SK_SWITCH_READINESS_THRESHOLD is 50u, a hardcoded constant
(capsule_vm_switch_signal.c:52). That is a fixed policy sitting in the middle of a
mechanism this section calls compudynamic — the same category §XIX rejected as "a different
hardcoded policy."
The layering does reconcile: the compudynamic turn-attractor (§XIX/§XXI) decides what work
is assigned, and the CPU-level switch signal is the mechanical follower that moves the
processor. Turn order in the meaningful sense is the attractor, and it is compudynamic as
ruled. But the constant 50 is a real seam, and it is the exact place where a hardcoded policy
could quietly start deciding turns. Flagged for later, not proposed as work here — it
predates this reshuffle and is FABRIC-3.md §XXVIII's own territory. Noted so it is not
discovered later and mistaken for something this document introduced. (FABRIC-4.md §1's
fixed-then-adaptive rate graduation is the precedent for how such a constant earns its way
to adaptive: measure against a fixed baseline first, self-tune only after.)
XV.4 — §XIV.4 reframed: the question was persistence; the answer is conservation
§XIV.4 asked whether in-flight messages must survive an arbiter's death, and warned that answering "yes" would force Hermes to hold durable state it could not safely write through Artemis at death-time. Captain Bob, on that item: "What survives the death of what? I don't understand that last part." Fair — it was posed as a persistence question, which is the wrong frame and the reason it read as opaque.
The right frame, given §XV.2. A message is not only content; it is heat. Traced
2026-09-18 in capsules/common/messaging.4th: MSG-ALLOC pulls Q.SLOT from the caller's own
reservoir before admitting a message (rolling back via STADIUM-RES-PUSH on refusal, line
127), and MSG-FREE-NODE releases it with STADIUM-EVICT (line 135-136). An arbiter dying
while holding N messages is holding N × Q.SLOT of fleet heat.
So the question is not "must the payloads be saved." It is:
Does a dying arbiter return the heat its undelivered messages were holding?
- Payloads may vanish. Consistent with §VII.1 step 3's "no lingering, no partial states."
- The heat may not.
K=vm_physics_fleet_heat_sum()is held atQ48_ONEand continuously verified (FABRIC-3.md§XVIII,fleet_k_q48/fleet_conserved). Heat that dies with the arbiter is heat that breaks a live invariant.
The answer to "what survives the death of what" is therefore: nothing survives except K.
This dissolves §XIV.4's hazard rather than resolving it. There is nothing to persist, so there is no death-time write, so there is no circular dependency on Artemis, so the stateless-arbiter shape §XIV.4 recommended is simply correct rather than a trade-off. And since death is compudynamic (§XV.2), a VM's death is already a Stadium eviction event — returning heat is not a special case bolted onto death, it is what dying consists of. §XIV.3's "Artemis for persistence" ruling stands untouched and applies to fleet/floor persistence; the arbiter simply never needed any.
The one thing to verify when this is eventually built (not a design question, an
implementation check): that every message-holding structure's teardown path actually reaches
STADIUM-EVICT, so a mass teardown returns heat rather than leaking it. fleet_conserved
already makes that directly testable — a leak shows up as K ≠ Q48_ONE, not as a silent
error. Added as punch item 16.
XV.5 — What these four rulings have in common
Three of the four answers reject the question rather than answer it. There is no death declarer, no turn-order owner, and nothing to persist — and in each case the thing that seemed to need an authority turned out to be an outcome of the physics instead. The one question that was answered directly (§XV.1, who may birth) was answered with "anyone," which also declines to create an authority.
That is the actual defence against becoming a conventional scheduler, and it is stronger than the firewall §XIV.5 proposed. A firewall constrains a component that owns something. These rulings mean there is no owner to constrain. The design's protection is structural, not procedural: authority only ever flows down the birth graph (§IX.2), and everything else — death, turn order, conservation — is physics rather than policy.
XV.6 — Punch list after these rulings
- ✅ Item 6 (§VI.3, "ACL/DNA" referent) — ANSWERED (§XV.1): inheritance by copy; existing
ACL-INHERITsemantics suffice; no fifthDictEntryfield. - ✅ Item 7 (§VI.4, "standing") — ANSWERED (§XV.1): decorative; reading (a) confirmed.
- ✅ Item 8 (§VII.3, ladder vs. detection) — WITHDRAWN (§XV.2): premise wrong, no detection mechanism required.
- ✅ Item 11 (§IX.3, who declares a birther dead) — PREMISE REJECTED (§XV.2): nobody; compudynamic death.
- ✅ Item 14 (§XIV.5, scheduler firewall) — ANSWERED (§XV.3), with a stronger invariant than the one proposed: no owner, not a named owner.
- ✅ Item 15 (§XIV.4, message durability) — DISSOLVED (§XV.4): reframed from persistence to conservation; nothing survives but K.
- ⬜ Item 16, NEW (§XV.4) — implementation check, not a design question: verify every
message-holding teardown path reaches
STADIUM-EVICTso mass teardown returns heat. Testable directly viafleet_conserved. - ⬜ Item 9 (§VII.2 —
SOSmessage-type number and its consumer) — still open. Note §XV.2 narrows it: with no death-declarer, anSOSconsumer cannot be a supervisor, so the question is now specifically what a peer does on receipt. - ⬜ Item 10 (§VIII.3 — the sinking semaphore's mechanism, the clean-shutdown routine's definition, and whether the window is bounded) — still open, and now the largest remaining design item.
- ⬜ Item 12 (§XIII.5 — is the Hestia Tripod leg a new singleton, distinct from today's per-attach proxies?) — still open, and still gates §IV.
Remaining open: items 9, 10, 12, 16. Item 10 is the substantive one; 9 is downstream of it, 12 is independent, and 16 is a build-time check rather than a decision.
XVI. Item 10 settled: clean shutdown is forced flush + BYE; Hera's is suicide (Captain Bob, 2026-09-18)
Captain Bob, verbatim: "A clean shutdown is a VM termination where flush is forced and the VM says BYE. Hera is the special case that should just halt the processor. Call it suicide I guess."
This settles §VIII.3's second open point (what a clean-shutdown routine concretely does) and, as a consequence, its third (is the window bounded). Traced 2026-09-18: the ruling is almost entirely existing mechanism, with exactly one behavioural change to a word that already exists.
XVI.1 — Every piece of this already exists
- Forced flush —
blk_flush(0).block_subsystem.c:988-990; the source comment states it directly: "0 is the 'flush all' sentinel." Already called that way atblock_subsystem.c:849, and already the Stadium's own write-back action forSTADIUM_BEHAVIOUR_MIGRATE(stadium.c:336,:373). Nothing new to build. - A child VM's
BYE—system_word_bye()(src/word_source/system_words.c:143-147): setsvm->halted = 1, signalling the REPL to stop. That is already precisely "a VM termination." Confirmed bymama_forth_words.c:2262-2263's own doc comment: "In child VMs this word is never registered; children use the standardsystem_word_byewhich setsvm->haltedand returns to the parent's REPL." - Halting the processor —
arch_halt(), declared atinclude/starkernel/arch.h:64and implemented on all three architectures (amd64/arch.c:207,aarch64/arch.c:126,riscv64/arch.c:170). Three-arch parity already holds, so the non-negotiable acceptance criteria are satisfiable without new per-arch work.
So a child VM's clean shutdown is blk_flush(0) then BYE — two existing calls in
sequence. This sits comfortably inside the "reorganization, not invention" scope discipline.
XVI.2 — The one real change: Hera's BYE does not currently halt. It cold-restarts.
This is the finding of this section, and it is a behavioural change to a live, registered
word — not a gap to fill. mama_word_bye() (mama_forth_words.c:2265-2271) is Hera's own
BYE, and its doc comment says outright: "Hera-only: reap all children then cold-restart the
machine." The body is exactly two actions:
capsule_vm_kill_all_nonmama(); /* "BYE: reaping children" */
arch_cold_reset(); /* "BYE: cold restart" */
Against Bob's ruling:
capsule_vm_kill_all_nonmama()already matches the "fleet shuts down" half. (The contract is documented downstream too —include/starkernel/capsule_birth.h:339describes it as called "from Hera's BYE immediately beforearch_cold_reset()to reap all children.")arch_cold_reset()does not match. The ruling is halt, not restart. A cold reset reboots the machine; suicide stops it. These are opposite outcomes, and today's code does the wrong one.
Implementation shape, traced: arch_cold_reset() is __attribute__((noreturn))
(arch.h:67), but arch_halt() is not — it halts only until the next interrupt and is
called once per idle iteration in normal use (amd64/arch.c:200-205's own comment). A
permanent halt is therefore arch_disable_interrupts() followed by a for(;;) arch_halt();
loop — which is exactly the pattern amd64's own arch_cold_reset() already uses as its
unreachable fallback (for (;;) __asm__ volatile ("cli; hlt");, arch.c:218) and the same
shape the kernel panic path uses. No new primitive; an existing pattern applied
deliberately.
Flagged for a conscious decision, not resolved here. .claude/CLAUDE.md carries a hard
rule: "Never modify a registered, tested word to 'fix' it." This is not a fix — it is a ruled
behavioural change — which is a different thing, and the rule's own reasoning ("the problem is
almost certainly in the caller") does not apply. But the change is real and should be made
knowingly rather than discovered in a diff. Whether Hera's suicide is spelled as a changed
BYE or as a separate word leaving BYE's cold-restart intact is an implementation choice,
and is not decided here. Worth noting that cold-restart-on-BYE is plausibly wanted behaviour
for an interactive operator typing BYE at Hera's REPL, which is an argument for two words
rather than one.
XVI.3 — A shared-source constraint on where the flush goes
system_word_bye() lives in src/word_source/system_words.c — vendored/shared source that
must compile and behave correctly in the hosted build as well as the kernel
(.claude/CLAUDE.md: gate kernel-only code with #ifdef __STARKERNEL__). Forcing a
blk_flush(0) inside system_word_bye() itself would change hosted-build behaviour for a word
that has nothing to do with this design.
So the forced flush belongs in the kernel-side shutdown path that calls BYE, not inside
BYE. Stated here as a constraint on implementation so the obvious-looking edit is not made
in the obvious-looking place.
XVI.4 — §VIII.3's third question answers itself: Hera's halt is the bound
§VIII.3 asked whether the shutdown window is bounded, noting that "holds it until it goes down"
gives no deadline. It is bounded, and by construction rather than by a timer: the window ends
when Hera halts the processor. Children flush and say BYE; Hera reaps whatever remains and
stops the machine. There is no deadline to choose, no timeout to tune, and — consistent with
§XV.3 — nothing that has to decide when time is up. The sequence terminates because its last
step is physically terminal.
This also satisfies §VII.1 step 3's "no lingering, no partial states" literally: after
arch_halt() in a disabled-interrupt loop there is no state left to linger in.
XVI.5 — Item 9 is now half-answered
§VII.2 left two questions on SOS: which message-type number, and "who consumes an SOS, and
what do they do with it?" The second is answered for the two fleet-fatal cases: on Hermes's
sinking semaphore (§VIII.1) and on total birther failure (§IX.1), the response is now concrete
— forced flush, BYE, and Hera's suicide as the terminal step.
Still genuinely open, and narrower than before: what a peer does on receiving an ordinary
VM's SOS. A single non-Tripod VM in trouble is presumably not fleet-fatal, so "everyone
shuts down" cannot be the universal answer — but §XV.2 rules out the obvious alternative, since
with no death-declarer an SOS consumer cannot be a supervisor that decides another VM's fate.
The remaining question is therefore specifically: is a peer's SOS actionable at all, or is
it purely advisory/diagnostic? Plus the trivial number allocation (§I.4: 5 and 6 are
unallocated gaps, 10 is next in sequence).
XVI.6 — Punch list after this ruling
- ✅ Item 10 (§VIII.3) — SETTLED (§XVI). Clean shutdown =
blk_flush(0)+BYE; Hera = suicide via a permanentarch_halt()loop. Window bounded by Hera's halt (§XVI.4). All mechanism exists on all three architectures. - 🔶 Item 9 (§VII.2) — HALF-ANSWERED (§XVI.5). Consumer action defined for the two
fleet-fatal cases. Open: whether a peer
SOSis actionable or advisory, plus the number. - ⬜ Item 12 (§XIII.5) — Hestia Tripod leg: new singleton or not? Unchanged; still gates §IV.
- ⬜ Item 16 (§XV.4) — implementation check: teardown paths must reach
STADIUM-EVICT. - ⬜ Item 17, NEW (§XVI.2) — decide whether Hera's suicide replaces
BYE's cold-restart or becomes a separate word. Small, but it changes a live registered word either way.
XVI.7 — One residual edge, raised not answered
If Hera is already dead, nobody performs the halt. §XV.2 makes death compudynamic and §IX.1
forbids any VM assuming her authority, so on total Hera failure there is no actor left to run
arch_halt(). The machine would be a live kernel with an empty Stadium floor — which is
precisely the "lingering, partial state" §VII.1 step 3 rules out.
The shape that resolves it without violating §IX: an empty floor is a kernel-observable condition, not a VM's decision. The kernel noticing "no live VMs remain" and halting is not a VM inheriting Hera's authority — it is the machine having nothing left to run. That keeps authority flowing only down the birth graph (§IX.2) while still terminating.
Offered as analysis, not a ruling — Captain Bob's call. Added as punch item 18.
XVII. Item 12 CLOSED — "Console" already exists as a singleton; it just has no VM face
Taken as closing item 12 (§XIII.5's Hestia-singleton question), the largest remaining design item and the one gating §IV.
§XIII.5 framed this wrongly, and the error is worth naming before the answer. It compared
the Tripod legs against capsule_console_birth()'s per-attach proxies, found Hestia "plural,
ephemeral and capsule-less," and concluded that a singular pinned Hestia "does not currently
exist" — so §IV.1 would be creating one, straining the reorganization-not-invention discipline.
That comparison picked the wrong object. Traced 2026-09-18: there are three distinct things
called "console" in this kernel, not two.
XVII.1 — The three consoles, traced
- The HAL console — the drawing fabric itself.
src/starkernel/hal/console.c(14 KB),framebuffer.c(13 KB),vt100.c(43 KB),font_8x16.c(27 KB) — ~98 KB of kernel C. Already singular. Already permanent. Already kernel-resident. It is not a VM and has no owner in the fleet sense. - The console proxy VMs —
capsule_console_birth(), plural, ephemeral, minted per attach from a 3-line C string literal (§XIII.5). - The active-console-VM name — a single global,
console_set_vm_name()/console_get_vm_name()(include/starkernel/console.h:190-191), swapped by every dispatch in the "switch, do work, switch back" pattern.
XVII.2 — RULING: the Hestia Tripod leg is (1), given a VM face. Not a promoted proxy.
The singular, permanent Hestia this design wants already exists in substance — it is the drawing fabric. What it lacks is an owner: today the fabric is an ownerless kernel singleton that any VM reaches into through a global. So §IV.1's "Hestia takes the vacated third slot" is not the invention §XIII.5 feared. It is giving an existing singleton a VM face.
That resolves the tension cleanly and without inventing anything:
- The Hestia leg is singular, pinned and born-at-boot — because the fabric it owns already is all three.
- The proxies are untouched. They stay plural and ephemeral, which is correct: they are what binding produces, not what Hestia is (§XIII.5's own phrasing, now properly grounded).
- §V.1's "bind point" falls out rather than being asserted. The VM that owns the fabric is necessarily what a user or agent VM binds to.
§XIII.5's proposed shape was right; its justification was wrong. It proposed exactly this leg/proxy split but called the leg new. The leg is not new — only its VM face is.
XVII.3 — The evidence that the fabric actually wants an owner
This is not merely tidy. The ownerless global in (3) has already caused a real, live bug,
documented in the kernel's own header (console.h:193-203): console_get_vm_name() alone is
not safe for save-then-restore, because it returns a pointer into the single internal
buffer that console_set_vm_name() copies into — so an intervening call in the normal
"switch, do work, switch back" pattern overwrites the very bytes the saved pointer points at
before the restore runs. Found live 2026-08-28; the restore silently no-opped.
console_save_vm_name() exists solely to work around it.
That is the signature of shared mutable state with no owner. Giving the fabric an owning VM is a structural fix for the class, not just a reorganization — and it is an argument for Bob's reshuffle that this document had not previously identified.
XVII.4 — What this commits to, concretely
Consequences of the ruling, so they are not discovered late:
- Hestia needs a capsule personality.
capsules/hestia/init.4th— a new directory besidehermes/andartemis/. This is the one genuinely new artifact, and it is a capsule, not a mechanism. is_fleet_foundationchanges membership (capsule_birth.c:793-796): the name-prefix triple becomes Hera / Artemis / Hestia. That single line is what makes a VM pinned (§XIII.3), so it is also what makes Hestia a Tripod leg.kernel_main.c:865'sS" Hermes" BIRTHbecomes Hestia's birth, and the switch-signal registration at:1007follows it.- The DoE CSV schema changes —
doe_log.c's six Tripod-named columns (§XIII.3). Still the expensive consequence, still not to be resolved by quietly renaming a column. - Hestia must have
BIRTHregistered (§XIII.6): binding mints a proxy, and minting isBIRTH. Already settled in principle by §XV.1's "any VM can birth." - Hestia becomes the owner of the
console_set_vm_name()discipline — the fix in §XVII.3 is available once there is an owner, but is not in this reshuffle's scope. Flagged, not scheduled.
XVII.5 — §IV is unblocked, and §XIII.5's marker corrected
With item 12 closed, §IV.1 (Tripod = Hera/Artemis/Hestia) stands as ratified with no outstanding objection, and §IV.2 remains dissolved per §XIV.2. §XIII.5's "this is the one place the scope discipline is genuinely strained" no longer holds and is corrected there.
XVII.6 — Punch list after this ruling
- ✅ Item 12 — CLOSED (§XVII). The Hestia leg is the existing drawing-fabric singleton given a VM face; proxies unchanged. §IV unblocked.
- 🔶 Item 9 — half-answered (§XVI.5): open is whether an ordinary peer's
SOSis actionable or advisory, plus the number allocation. - ⬜ §VIII.3 first bullet — where the sinking semaphore mechanically lives.
- ⬜ Item 16 (§XV.4) — implementation check: teardown paths must reach
STADIUM-EVICT. - ⬜ Item 17 (§XVI.2) — does Hera's suicide replace
BYE's cold-restart or become a separate word? - ⬜ Item 18 (§XVI.7) — if Hera is already dead, nobody performs the halt.
Nothing structural remains open. Items 9, 17 and 18 are small, local decisions; §VIII.3's
first bullet is a placement question; item 16 is a build-time check. Every load-bearing
architectural question this document opened — what Hermes becomes, what the Tripod is, what
BIRTH means, who owns death, who owns turn order, what survives, what shutdown is — is now
ruled.
XVIII. The Hestia component, designed in full (2026-09-18)
§XVII settled what the Hestia leg is. This section designs the component. Everything below
traced 2026-09-18 against e56974e; decisions are marked, proposals are marked, and open
questions are marked.
XVIII.1 — Hestia is three layers, and only the middle one is new
| Layer | What | Where it lives today | Change |
|---|---|---|---|
| 0 — the fabric | Raw output: framebuffer, VT100, glyph raster, UART | hal/console.c, framebuffer.c, vt100.c, font_8x16.c (~98 KB C) |
None. Stays kernel HAL. |
| 1 — Hestia the VM | Tripod leg. Owns fabric policy, is the bind point | Does not exist | New — and it is a capsule, not a mechanism. |
| 2 — console proxies | Per-attach relay VMs, one per bound user/agent | capsule_console_birth() |
None, except their birther becomes Hestia. |
The design is almost entirely layer 1, and layer 1 is mostly a relocation of vocabulary that already exists in the wrong dictionary (§XVIII.3).
XVIII.2 — The constraint that shapes everything: the HAL must never know a VM exists
This is not a preference, it is a defended invariant with a documented rationale
(include/starkernel/console.h:220-224, verbatim):
console.cis a clean HAL module with no dependency on capsule/WIREBIND logic (zuse_session,capsule_wirebind_attached_username()) — pulling either in directly here would be a real layering violation, not just a style preference.
So Hestia-the-VM may not be implemented by making console.c VM-aware. Ownership flows one
way only: Hestia reaches down into the HAL; the HAL never reaches up.
The sanctioned pattern for the cases where the HAL does need something VM-shaped already
exists and should be reused rather than reinvented: console_set_user_prefix_provider()
(console.h:231) — a callback that lets repl.c supply the "user" half of the [user@VMName]
prompt "without console.c knowing anything about VMs, sessions, or WIREBIND." Any further
HAL→Hestia coupling this design needs takes that shape: a registered callback, never an
include.
Consequence, stated plainly: Hestia's "ownership" of the fabric is by convention,
vocabulary and registration — it is the VM that holds the drawing words — not by any
enforcement inside console.c. That is weaker than it sounds and is exactly right: it keeps
the HAL reusable and the layering intact.
XVIII.3 — Hestia's dictionary: 1,051 lines of Hestia vocabulary currently live in Hera
The most concrete finding in this section. capsules/fabric.4th's own header reads:
fabric.4th— Console drawing-fabric coordinate machinery.FABRIC-0.mditem 4.3.3. 45-degree cavalier orthographic projection... Raw pixel write (PLOT/FB-WIDTH/FB-HEIGHT) is C; this capsule is the FORTH-side policy on top of it.
It is named Hestia's fabric, it is the FORTH-side fabric policy — and it loads into Hera's
dictionary (capsules/init.4th block 2049: S" fabric.4th" EXEC, S" font.4th" EXEC).
| Capsule | Lines | Loads into today | Should load into |
|---|---|---|---|
fabric.4th |
319 | Hera (init.4th b2049) |
Hestia |
font.4th |
733 | Hera (init.4th b2049) |
Hestia |
turtle.4th |
99 | sdk.4th (opt-in, not boot) |
unchanged — it is SDK, not fabric |
Plus the C primitives underneath: PLOT, FB-WIDTH, FB-HEIGHT
(src/word_source/framebuffer_words.c:62-64). Their registration moves to Hestia's word
table; the implementations do not change.
DECIDED (proposal — Captain Bob's call, but this is the direct consequence of §XVII.2):
fabric.4th and font.4th move from init.4th to capsules/hestia/init.4th. This is a pure
relocation of 1,051 lines — the single most obviously-correct edit in the whole reshuffle, and
the clearest evidence that the fabric wants an owner: it already has the vocabulary, just
attached to the wrong VM.
Parity consequence (§III.5): Hera's dict_hash will shrink, Hestia's is new. Per §XX's
standard, the property that matters is cross-architecture identity, not an unchanging absolute.
XVIII.4 — Hestia's capsule personality
capsules/hestia/init.4th — a new directory beside hermes/ and artemis/, and the one
genuinely new artifact this reshuffle creates.
Contents, in load order:
S" common:messaging.4th" EXEC+MSG-CD-INIT— Hestia is a Tripod leg and a full messaging participant. Note this differs from the proxies, which deliberately skipCOMMON-CH(capsule_console.c:23-26: "a console's own traffic is direct 1:1 with its paired user VM"). The leg subscribes; the proxies still do not.S" fabric.4th" EXECandS" font.4th" EXEC— per §XVIII.3.- Hestia's own bind vocabulary (§XVIII.5).
- A
WELCOME/banner word, matchinghermes/init.4th's andartemis/init.4th's shape.
Block allocation is a real to-do, not a formality. mkcapsule's actual constraints, per
§XXXII.2's correction: block range [2048, 5120) and a hard 16-content-line-per-block
cap (not the 1024-byte framing .claude/CLAUDE.md still describes — that doc error is
outstanding). Block 4997 is already taken by the proxy's own C string literal
(capsule_console.c:28-31), so Hestia's range must avoid it. Allocate against
capsule-reserved.txt and verify with mkcapsule --lint capsules/ (§XXIV's collision
machinery covers this; it is blind to content, only to numbers).
XVIII.5 — The bind point: flow and vocabulary
Binding, end to end, after the reshuffle:
human attaches thumbdrive agent VM needs a console
│ │
▼ ▼
WIREBIND path CONSOLE-ATTACH
│ │
└──────────► Hestia ◄───────────┘
│
BIRTH a proxy (§XV.1 makes this legal:
│ any VM may birth)
▼
proxy paired to target
(VM-NAME-REG, unchanged)
CONSOLE-ATTACH ( name-c name-u -- ok? )already exists and is live-verified (§I.6, closed 2026-09-16). It resolves the target's liveness before birthing, so a typo refuses cleanly with no orphaned VM. Keep it exactly as is. The change is who runs it: today the threecapsule_console_birth()call sites run in Hera's or the caller's context (capsule_wirebind.c:245,mama_forth_words.c:1591,:2015). Under this design Hestia is the birther.- This is why Hestia needs
BIRTHregistered (§XIII.6) — binding mints a proxy, and minting isBIRTH. Already legal per §XV.1. - Mechanically this already works: two of the three call sites pass
vm->stadium_vm_id— whichever VM invoked — so parentage is already generic (§I.5, §XIII.6). Only registration changes.
OPEN — agent-VM binding and the ~user convention (§V.3.1, still unresolved). The pairing
check in sk_repl_dispatch_line() reconstructs the target as console_get_vm_name() + "~user"
— a convention built for human at a console → identity VM. A GPIO VM (FABRIC-4.md §2) is
not a ~user identity. §XXXII.2 records that a console named anything else "silently falls
back to direct interpretation with no error," caught live. Two shapes, neither chosen:
(a) widen the convention so the suffix is a property of the binding, not hardcoded; (b) give
agent binding its own word alongside CONSOLE-ATTACH. (a) is preferable on
no-bespoke-gate grounds but touches a live, bug-prone path; flagged rather than decided.
XVIII.6 — Headless-until-login survives, and the invariant that makes it survive
Hestia the leg is born at boot. A console session is not. These must not be conflated, or §VIII.1's ratified headless-until-login policy reopens — the exact failure §XXXII.2 warned about for unattended birth.
INVARIANT, to be stated in whatever code implements Hestia:
Hestia's birth at boot must not set
g_wirebind_attached_username, must not causesk_console_identity_present()(repl.c:122) to report an identity, and must not mint a proxy. Hestia owns the fabric from boot; it presents nothing until something binds.
This is the same invariant §XXXII.2 imposed on unattended identity birth, applied to a second path. It is cheap to hold — Hestia births no proxy until asked — but it is exactly the kind of thing that gets violated by a well-meaning "initialise the console at startup" line.
XVIII.7 — What Hestia does not own
Stated because a component that owns "the bind point" attracts responsibilities that belong elsewhere, and this document has already had to defend against exactly that (§XV.3):
- Not turn order. Nothing owns it (§XV.3).
- Not routing. That is kernel-Hermes (§III), which sits below Hestia.
- Not the lifecycle of anything but its own proxies. Hestia births proxies; it does not birth, kill, or supervise identity VMs. Authority flows down the birth graph only (§IX.2).
- Not the prompt's user half. That stays
repl.c's, supplied to the HAL via the existing provider callback (§XVIII.2). Moving it into Hestia would be the layering violationconsole.hexplicitly names.
XVIII.8 — Hestia's own failure behaviour
Hestia is a Stadium-floor VM above the routing layer, so §VIII.2's rule applies without an
exception: Hestia emits SOS; only Hermes uses the semaphore. Its clean shutdown is
§XVI's: forced blk_flush(0), then BYE.
A pleasant property worth recording: Hestia's death degrades gracefully by construction. Because layer 0 is HAL C (§XVIII.1) and ownership is by convention rather than enforcement (§XVIII.2), a dead Hestia leaves the framebuffer, VT100 and UART fully functional — the fabric simply becomes ownerless again, which is precisely today's status quo. The kernel can still print. This is the opposite of Hermes, whose death takes the transport with it. So the Tripod's three legs have three genuinely different failure characters: Artemis's death costs storage arbitration, Hestia's costs only fabric policy, Hermes's costs the transport itself — which is why only Hermes needed the semaphore.
XVIII.9 — Punch list for Hestia
Design items, in dependency order. No code authorized.
- ⬜ Ratify §XVIII.3's relocation:
fabric.4th+font.4thmove frominit.4thtocapsules/hestia/init.4th;PLOT/FB-WIDTH/FB-HEIGHTregistration moves to Hestia. - ⬜ Allocate Hestia's block range against
capsule-reserved.txt, avoiding 4997; verify withmkcapsule --lint(§XVIII.4). - ✅ SETTLED 2026-09-19 by §XX — neither option. The pairing is already recorded at bind
time; the
~userreconstruction is a redundant derivation and is removed. Agent binding then needs no new mechanism. - ⬜ Confirm Hestia in the
is_fleet_foundationtriple (capsule_birth.c:793-796) and its birth atkernel_main.c:865, replacing Hermes's (§XVII.4). - ⬜ Handle the
doe_log.cCSV schema change (§XIII.3) — still the expensive consequence. - ⬜ State §XVIII.6's headless invariant in the implementation.
- ⬜ Not in this reshuffle: the
console_set_vm_name()ownerless-global fix (§XVII.3) is now available once Hestia exists, but remains out of scope. Flagged, not scheduled.
XIX. RULING: the third Tripod leg is named Hestia, not Console (Captain Bob, 2026-09-19)
The Tripod is: Hera, Artemis, Hestia. Applied throughout this document — 99 occurrences renamed; three verbatim quotations deliberately left saying "Console" (§XIX.4).
XIX.1 — The naming convention, stated so it stops being re-litigated
antiprosopos (Gk. representative/delegate) was considered first and rejected, on Captain
Bob's reasoning, which is the correct reasoning and is recorded here because it generalizes:
"Now this is just a name of a Greek mythos god, it doesn't have to carry full/perfect [fidelity], it's only a contextual display... After all, Artemis isn't exactly the perfect name for a memory manager/storage manager either."
That is the convention, and it had never been written down: a Greek deity name, evocative rather than literal. Checked against what exists — Hera (queen/mother → the Mama VM) is a close fit, Hermes (messenger → messaging) is the closest, and Artemis (huntress, wilderness → block storage and freemap arbitration) is a loose fit that has never caused anyone a problem. Fidelity was never the standard.
antiprosopos failed on being the wrong kind of word: a common noun straining for literal
accuracy, which is the opposite of how the other three work. Hestia — goddess of the hearth —
is a deity, is evocative (the hearth is the fixed centre of the household, where everyone
gathers), and carries a pleasing incidental: Hestia is the one who never leaves. That maps
onto the pinned-session model (FABRIC-2.md §H.1, "they never leave, permanently") without
having been chosen for it.
Two costs that antiprosopos carried are retired by this choice, not merely tolerated:
- Pattern.
antiprosoposis Greek vocabulary, not a deity — it half-joined the set.Hestiajoins it fully. - Typo surface. Twelve characters of unusual spelling, in a path where a VM-name mismatch
fails silently (§XIX.5).
Hestiais six, comparable toHermesandArtemis.
XIX.2 — Why renaming at all is structural, not cosmetic
§XVII.1 found three distinct things in this kernel all called "console" — the HAL fabric, the VM, and the per-attach proxies — and that collision is exactly what led §XIII.5 to the wrong conclusion about whether a singular Console existed. Naming the VM separately resolves it by construction:
| Layer | Name after this ruling |
|---|---|
| 0 — the drawing fabric (HAL C) | the console — console.c, unchanged |
| 1 — the Tripod leg, owns the fabric | Hestia |
| 2 — per-attach relay VMs | console proxies — unchanged |
"Hestia owns console.c" is unambiguous in a way "Console owns console.c" cannot be. This
document has already paid once for that ambiguity; the rename is what stops it recurring.
XIX.3 — What is NOT renamed
The ruling names a VM. It does not rename the subsystem:
- The HAL stays
console.c/console.hand everyconsole_*()function. Renaming them would be churn across ~98 KB of C for no gain, and would re-create the confusion by making the HAL sound like the VM. capsule_console_birth()/CONSOLE_IDENTITY_SRCstay — they mint console proxies, still called console proxies.CONSOLE-ATTACHstays..claude/CLAUDE.md's "never modify a registered, tested word" applies with more force to renaming one, and the word remains accurate: it attaches a console. Recommended, not ruled.CONSOLE-CMD-EVENTandsk_console_identity_present()stay — both are layer 0/2 business (a console session), not Hestia's (§XVIII.6).
Net code-visible rename surface: the VM's registry name, its capsule directory
(capsules/hestia/init.4th), and the places the Tripod is enumerated (§XIX.6).
XIX.4 — Three quotations deliberately still say "Console"
Quoted source is not silently edited to match a later decision:
- Line 26 — the opening brief's own wording ("Hera lifecycle/ACL, Console framebuffer").
- §XVIII.3 —
capsules/fabric.4th's verbatim header, "Console drawing-fabric coordinate machinery." That is what the file says today; it changes when the file is edited, and that edit belongs to the §XVIII.3 relocation, not to this ruling. - §XVII's heading — a meta-reference to the name itself, which is that section's subject.
XIX.5 — ~user is a separate question, unaffected
antiprosopos was first raised as a replacement for the ~user registry-name suffix. That
remains punch item 3 (§XVIII.5), and the Hestia ruling does not touch it. Recorded so the two
never get conflated:
Traced 2026-09-18 — as a suffix it does not fit the buffers. VM_NAME_MAX is 64 and
USER_IDENTITY_USERNAME_MAX 32, so the name fits in principle, but every construction buffer
is sized + 8, tuned exactly to "~user" (5 chars + NUL = 6): capsule_wirebind.c:222 (=40),
mama_forth_words.c:1855/:1991 and repl.c:1243 (=72), each guarding on a hardcoded + 6.
A 14-byte suffix would not overflow them — it would make them refuse, silently failing to
bind usernames over 26 characters and registry names over 58. Six memcpy sites and five
guards hardcode that 6, and FABRIC-3.md §XXVI was itself a truncation fix.
And it would not answer item 3 regardless: a GPIO VM is neither a user nor a human's representative. The structural fix is making the suffix a property of the binding; settle that first and land any suffix rename inside it, since the same eleven sites are touched either way.
XIX.6 — Edit surface
All were already edit sites for the Tripod membership change (§XVII.4); the rename changes the string, not the count:
capsule_birth.c:793-796—is_fleet_foundation's prefix triple becomes Hera / Artemis / Hestia (matched byvm_name_prefix_eq_nocase, as today).kernel_main.c:865—S" Hestia" BIRTH; the liveness check and the switch-signal registration at:1007follow.capsules/hestia/init.4th— the new capsule directory (§XVIII.4).doe_log.c— CSV columns becomehestia_heat_q48andswitch_hestia_readiness. Still the expensive consequence (§XIII.3), buthestiais the same width ashermes, so the schema churn is a rename rather than a reflow.
XIX.7 — One hazard, recorded and generalized
A VM-name mismatch in this system fails quietly. §XXXII.2 records it caught live on the
first attempt: a console named anything other than the expected form "silently falls back to
direct interpretation with no error" — typing 5 6 + . printed a direct 11 instead of a
relayed one, with nothing reported.
Hestia is short and ordinary enough that this is no longer an argument about the name. But
the class remains: if the §XVIII.5 binding work touches that path anyway, making an
unresolved or mismatched name say so would retire the hazard for every VM name, not just this
one. Raised as punch item 19, not scheduled.
XX. Item 3 SETTLED: delete the suffix from the mechanism — neither widen it nor add a word
§XVIII.5 posed agent-VM binding as a choice between (a) widening the ~user convention and
(b) a sibling word for agent binding. Both options are wrong, because both accept a premise
the code does not support. Traced 2026-09-18/19: the pairing is already recorded
explicitly, and the ~user reconstruction is a redundant second derivation of information the
mechanism already holds.
XX.1 — The relay does not route by name. It routes by a slot recorded at bind time.
sk_repl_dispatch_line() (repl.c:1225-1265) does two separate things, and only one of them
involves the suffix:
- The guard builds
paired_name = console_get_vm_name() + "~user"(repl.c:1243-1247) and checkscapsule_vm_find_by_name(paired_name, ...)isVM_STATE_LIVE. - The actual relay then sends
CONSOLE-CMD-EVENT 0 3 S" <line>" 0 MSG-SEND— to index 3, with this comment verbatim: "to-index 3: the fixed convention this console's ownVM-NAME-REGentry for its paired user VM uses (set once at pairing time — see the pairing word)."
PAIR-TEST's own header says the same from the other side (mama_forth_words.c:1542-1543):
"Registers the pairing in the console's own VM-name routing table at the fixed index (3) that
relay uses."
So the target is already stored, per-proxy, at bind time. The suffix string is used only to answer "is my partner alive?" — a question the stored pairing can answer directly, without deriving anything.
XX.2 — This is the documented cause of the known silent-failure bug
CONSOLE-ATTACH's own doc comment (mama_forth_words.c:1931-1940) states it outright:
sk_repl_dispatch_line()'s own pairing check (repl.c) reconstructs the target asconsole_get_vm_name() + "~user"... A console named anything other than the identity's own base name reconstructs the wrong target string and silently falls back to direct interpretation — no error, just quietly never relays.
CONSOLE-ATTACH already carries a workaround for this: it "takes exactly one name, not an
independently chosen console name," collapsing two arguments into one so the reconstruction
cannot disagree. That is a constraint imposed by the derivation, not by the problem.
XX.3 — RULING (proposed): the bind records its target; nothing re-derives it
Replace the derivation with a read of what bind time already stored.
sk_repl_dispatch_line()'s guard asks "does this console have a recorded pairing, and is that target live?" instead of "does<name>~userexist?"CONSOLE-ATTACHresolves the target as given, rather than appending~user(mama_forth_words.c:2000).~usersurvives as a naming convention, not as a mechanism. It still disambiguatesrajamesthe proxy fromrajames~userthe identity in the prompt — the whole point of the[user@VMName]form (§XXV follow-up, 2026-09-13). It simply stops being load-bearing.
Four things fall out, none of which needed designing:
- Agent-VM binding stops being a special case. A GPIO VM named
gpiobinds by the same unchanged path asbob~user, because nothing appends anything to its name. There is no agent-binding mechanism to build. CONSOLE-ATTACH's one-name constraint can relax if ever wanted — it exists only to prevent a reconstruction that no longer happens. Not proposed as work; noted so the constraint is not later mistaken for a requirement.- The silent-fallback class is dissolved rather than fixed. With no string to mismatch, the states are "a pairing is recorded" or "none is" — determinate and reportable. This retires punch item 19 (§XIX.7's "make mismatch loud") by removing what could mismatch, which is strictly better than making it noisy.
- The suffix-rename question becomes purely cosmetic (§XIX.5). Once lookup no longer derives the suffix, the remaining sites only ever construct a name.
XX.4 — The six memcpy sites split cleanly in two
This is what makes the change small. Traced 2026-09-18:
| Site | Role | Under this ruling |
|---|---|---|
capsule_wirebind.c:229 |
names the identity VM at birth | stays — naming |
mama_forth_words.c:1864 (UNATTENDED-BIRTH) |
names the registry entry | stays — naming |
mama_forth_words.c:1579 (PAIR-TEST) |
names both halves; diagnostic word | stays — naming |
repl.c:1247 |
re-derives the partner for a liveness guard | goes |
mama_forth_words.c:2000 (CONSOLE-ATTACH) |
re-derives the target | goes |
Only the two derivation sites change. The naming sites are untouched, which is why ~user
can remain a convention without remaining a mechanism.
XX.5 — What "binding" means for an agent VM, stated because §V.1 conflated two things
§V.1 called Hestia "the bind point for users and agent VMs." Worth separating, since the two need different things and only one of them is binding:
- Reachability — being addressable by other VMs — is kernel-Hermes's job (§III), not Hestia's. A GPIO VM is reachable because routing exists, not because it bound to anything.
- Fabric access — a VM that wants to draw — is vocabulary and ACL, not binding: it
needs Hestia's drawing words (§XVIII.3), gated the
ACL.4thway if gated at all. - Binding proper — acquiring a console proxy so a human can type at a target — is what
CONSOLE-ATTACHdoes, and after §XX.3 it works for any live VM by name.
So an agent VM does not routinely bind at all. It binds only when a human wants to attach to it for inspection or control — and that is the ordinary console path, unchanged. The "agent-binding problem" was an artifact of the hardcoded suffix; removing the suffix removes the problem rather than solving it.
XX.6 — Punch list
- ✅ Item 3 — SETTLED (§XX.3): the bind records its target; nothing re-derives it. Agent binding needs no new mechanism.
- ✅ Item 19 — RETIRED (§XX.3.3): dissolved by removing the mismatch, not by making it loud.
- 🔶 Item 9 — unchanged: is an ordinary peer's
SOSactionable or advisory, plus its number. - ⬜ §VIII.3 first bullet — where the sinking semaphore mechanically lives.
- ⬜ Items 16, 17, 18 — unchanged (teardown/
STADIUM-EVICTcheck;BYEvs. a separate suicide word; the empty-floor halt).
Caveat on scope, per §XIV.1. repl.c's relay and the routing-slot-3 convention are part of
the FORTH messaging layer Captain Bob ruled legacy. This ruling states the principle — the
pairing is recorded, never derived — which kernel-Hermes must carry forward. It is not a
proposal to patch the legacy path in place.
XXI. Item 9 SETTLED: SOS travels up the birth graph. It is actionable there and advisory everywhere else.
§VII.2 left two questions: who consumes an SOS and what do they do with it, and which number.
The first turns out not to need a new rule — §IX.2's "authority only ever flows down the
birth graph" already answers it, and answering it supplies something this document had been
missing without noticing.
XXI.1 — RULING: actionability is a property of the relationship, not of the message
- To the sender's birther — actionable. The parent may re-birth. This is the parent exercising its own birth authority (§XV.1: any VM may birth), not inheriting the child's. §IX.2 is untouched: nothing flows sideways or up.
- To every other VM — advisory. A peer may adjust its own behaviour (stop sending to a failing VM, note it, shed work that depended on it). A peer may not act on the sender's behalf, re-birth it, or reclaim anything of its own.
One rule, no exceptions list: an SOS is actionable exactly where the birth graph already
grants authority.
XXI.2 — Why this does not smuggle in a supervisor
Pressure-tested against the doctrine and the rulings, because "parent watches child and restarts it" is precisely the shape §XIV.5 warned about:
- Message-driven, not polling. The parent acts on receipt, during its own tick. That is
the doctrine's own sanctioned form —
VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md: "a VM's own internal state (e.g. Hermes's message queue) determining whether it does anything." No timer, no scan, no watch loop. - Optional, never obligatory. A parent that ignores an
SOSis behaving correctly. Nothing polices it. - No declarer. The sender reports its own trouble. Nobody judges anyone else's liveness, so §XV.2 holds exactly as ruled.
- No authority transfer. The parent uses authority it already had, on a VM it already birthed.
A supervisor is a component that must watch and may decide another's fate. This is neither.
XXI.3 — What this actually closes: the ladder's missing actor
§VII.1's ladder has three rungs, and until now only the first and third had an obvious actor.
With SOS settled, each rung has exactly one:
| Rung | Actor |
|---|---|
| 1. Recover gently (warm restart) | The VM itself, while still alive — self-applied (§XV.2) |
2. Escalate to a from-scratch BIRTH |
The birther, on receipt of the SOS |
| 3. Fail brutally | Nobody. Compudynamic death (§XV.2) |
And this is why §IX.1's fleet-shutdown rule is not a special case. Hera has no birther —
capsule_birth.c:178 sets her name at the root, and the Tripod legs are birthed by her
(kernel_main.c:865). So for Hera, rung 2 has no actor, and the ladder falls straight through
to rung 3. §IX.1's "no provisional handoff → the fleet runs its own shutdown" is not an extra
rule bolted on for Hera; it is what the ladder already does when you have no parent.
The ladder and §IX are the same rule seen from two ends: rung 2 exists if and only if you have a birther.
XXI.4 — An SOS must not carry an executable payload
A real hazard specific to this system, worth stating before anything is built. In the existing
mechanism a message's payload is FORTH text executed by the receiver — messaging.4th
block 5041 says so directly of BLK-ATTACH-EVENT: "Payload is FORTH text, executed by the
receiver, same as every other message here."
An SOS whose payload were executed by the birther would let a failing VM drive its own
parent — precisely inverting the authority direction §XXI.1 exists to preserve, and doing it
at the moment the sender is least trustworthy.
So: an SOS payload is identifying and descriptive only — who is failing, and what for —
never instructions. The birther decides what to do; the sender only reports. This is a
deliberate departure from the "payload is executed text" convention, and the reason is the
whole point of the message.
XXI.5 — SOS is best-effort, and that is not a gap
A VM that dies instantly emits nothing. There is no SOS, nobody notices, and per §XV.2
nobody declares anything — it is simply gone.
This is correct, not a hole to plug. Covering it would require something watching for
absence, which is a detector, which §XV.2 rules out. SOS covers exactly the case where a VM
can see its own failure coming; the silent case is covered by nothing, deliberately.
(FABRIC-3.md §XX's wellness-check idea remains the separate, optional thing that could
address it — on its own merits, not as a dependency of this design. §VII.3 was withdrawn for
assuming otherwise.)
XXI.6 — RULING: SOS takes 10. Do not reuse the gaps.
Verified 2026-09-19: message types in use are 1, 2, 3, 4, 7, 8, 9; 5 and 6 are genuinely
unallocated — a repo-wide search for either as a message-type constant returns nothing (the
sole 6 is CH-CELLS, unrelated). Status sentinels sit at the top: MSG-NACKED 253,
MSG-DELIVERED 255.
Take 10, the next in sequence, rather than filling a gap. The reasoning is legibility across the transition: during the dead-FORTH cleanup (§XIV.1) legacy constants and the new kernel enumeration coexist, and logs will span both eras. Monotonic allocation means a number appearing in any log means exactly one thing, forever. A reused gap would make an old log ambiguous for no gain. The gaps stay gaps; a documented hole is more honest than a silent reuse.
Scope note per §XIV.1: SOS properly belongs to kernel-Hermes's own message-type
enumeration, since the FORTH layer is legacy. The legacy numbering is not binding on the new
enum — but it should be honoured as a floor, for the same log-legibility reason.
XXI.7 — Named consumers, so this does not become another SPAWN-EVENT
§I.4 recorded the warning: SPAWN-EVENT was grepped across the whole tree and has zero
consumers — "an unwired placeholder, not working infrastructure." A message type with no
dispatcher is exactly what SOS cannot be.
So, stated explicitly: SOS's consumer is the sender's birther, whose handler is the
rung-2 re-birth decision (§XXI.3), plus any peer that chooses to shed dependence on the
sender (advisory, optional, and correct to omit entirely in a first implementation). If no
birther is live, the SOS is simply never consumed — a determinate outcome, not a silent gap,
because the birth graph says plainly whether a consumer exists.
XXI.8 — Punch list
- ✅ Item 9 — SETTLED (§XXI). Actionable to the birther, advisory to peers; payload descriptive, never executable; number 10; consumer named.
- ⬜ §VIII.3 first bullet — where the sinking semaphore mechanically lives. Now the last open design question in this document.
- ⬜ Items 16, 17, 18 — the
STADIUM-EVICTteardown check;BYEvs. a separate suicide word; the empty-floor halt when Hera is already gone.
XXII. Execution method: the surgical strip is punch item 1, and it is iterative (Captain Bob, 2026-09-19)
Captain Bob, verbatim: "As far as any of the existing forth we anticipate will no longer be used. That's probably going to get totally stripped out before we code. The existing gaps we need to fill in will reveal themselves as we code/test iterate and dead FORTH code falls out at the same time. That's going to be the first very surgical item on the punchlist."
So the method is: strip the anticipated-dead FORTH first, then code; further dead FORTH falls out as coding and testing reveal the real gaps, and gets stripped in the same loop. This becomes punch item 1, ahead of everything else in §XVIII.9 and §XX.
XXII.1 — Two categories, and only one of them can be stripped first
"Strip before we code" must not be read as "delete the working system before the replacement exists." The legacy FORTH splits cleanly, and the split is the ordering:
- Category A — dead today, independent of this reshuffle. Nothing loads or invokes it now. Strippable immediately, before a line of new code, with no dependency on kernel-Hermes existing. This is the genuine first item.
- Category B — dead on arrival of kernel-Hermes.
capsules/hermes/init.4th, most ofcapsules/common/messaging.4th, the routing table, the slot-3 pairing convention (§XX.1). These are load-bearing until the replacement boots. Stripping them first would remove a working messaging layer with nothing behind it. They fall out during the iterate loop, as Captain Bob describes — not before it.
§XIII.1's fourteen-site inventory is the map for both; §XIII.2 already identified the Category A members it found.
XXII.2 — The trap: reachability here cannot be established by grep, in either direction
Recorded because this document nearly walked into it while building the strip inventory. A
first pass counted textual references per capsule and produced a list with
init-l8-diverse.4th, init-l8-omni.4th, init-l8-stable.4th, init-l8-temporal.4th,
init-l8-transition.4th, init-l8-volatile.4th, sdk.4th and others at zero references —
i.e. apparently dead.
They are not dead. They are DoE experiment infrastructure, and a second pass including
experiments/, tools/ and docs/ found 18–19 references each for the init-l8-* family
and 9 for sdk.4th. The first pass searched only capsules/ and src/.
The failure is structural, not a careless grep:
- It under-reports. Capsules are birthed by name from runtime strings —
S" <name>" BIRTH, campaign scripts,EXECof a name assembled at run time. Static reference counting cannot see any of that. - It over-reports. A capsule named for a common word returns noise:
processscores 180 hits across the tree, essentially all prose. (process.4th's actual deadness was established the other way — by confirming nothingEXECs it — not by counting.)
So no strip list in this document is authoritative, including any list this document
produces. Reachability must be established per capsule by all three routes: loaded from a
boot path, invoked from experiments//tooling, or present in the capsule directory mkcapsule
bakes into the image.
XXII.3 — The acceptance asymmetry, which is what makes "surgical" necessary
.claude/CLAUDE.md is unambiguous that the three-architecture QEMU boot is the only acceptance
test there is. For this particular task it is necessary but not sufficient, and the gap is
specific:
- A boot exercises the boot path. Deleting something Category A and boot-reachable fails loudly and immediately — good.
- A boot does not exercise DoE/experiment capsules. Deleting one passes all three architectures cleanly and surfaces months later, when a campaign is run.
That asymmetry is the real hazard of this item. And the stakes are not only code:
.claude/CLAUDE.md records that the logs/ artifacts "are audit artifacts — they are
committed to the repo. Do not delete them," and that the DoE campaign report is patent
support material. Experiment infrastructure is part of a measurement apparatus with an
evidentiary role.
Rule, proposed: treat every experiments/-reachable and DoE-related capsule as live by
default. Removing one is its own decision with its own justification, never a side effect of
a messaging cleanup.
XXII.4 — How the loop should run
Consistent with this series' own discipline and with the acceptance criteria:
- Strip Category A only, in a commit separate from any new code, so a single file can be restored without unpicking a feature. Nothing else in the same commit.
- Boot all three architectures after the strip, before writing anything new — establishing that the strip alone is clean, rather than discovering later which half of a mixed commit broke something.
- Then code, and let the gaps reveal themselves as Captain Bob describes.
- Each time coding proves a Category B item dead, strip it in its own commit too, with its own three-arch boot.
Expected and not a defect: every strip changes dict_hash. Per §III.5 and §XX's standard,
the property that must hold is cross-architecture identity, never an unchanging absolute
value. mkcapsule --lint capsules/ must stay clean throughout, and freed block ranges should
be returned to capsule-reserved.txt rather than silently reused (§XVIII.4).
XXII.5 — What rides along
- The
MANIFEST.mdcorrections (§XIII.2, formerly item 13) belong to this pass: block 4055's "immutable ABI" claim thatFABRIC-2.md:2773already declared stale, and block 2049's wrong contents list. Correct them as the files they describe are stripped, so the manifest never describes a file that no longer exists. SPAWN-EVENT(§I.4: zero consumers, confirmed twice) is Category A by definition.src/*.c.bak—.claude/CLAUDE.mdflagsvm.c.bak,doe_metrics.c.bakandinference_engine.c.bakas tracked-but-stale repo hygiene debt, to "report it if it comes up; don't delete unprompted." It has now come up. Not part of this reshuffle, but the natural companion pass — flagged, not scheduled, and still requiring its own authorization.
XXII.6 — Punch list, re-ordered
1. ⬜ SURGICAL STRIP — Category A dead FORTH. Establish reachability per §XXII.2's three
routes (never by grep alone), strip in isolated commits, three-arch boot each time,
mkcapsule --lint clean, MANIFEST corrected alongside, freed blocks returned. Precedes all
other items.
Then, unchanged in content but now downstream of it:
- ⬜ Hestia: relocate
fabric.4th+font.4th; movePLOT/FB-WIDTH/FB-HEIGHTregistration (§XVIII.9.1). - ⬜ Hestia's block range against
capsule-reserved.txt, avoiding 4997 (§XVIII.9.2). - ⬜ Hestia into
is_fleet_foundation;kernel_main.c:865birth; switch-signal registration (§XVIII.9.4, §XIX.6). - ⬜
doe_log.cCSV schema change (§XIII.3) — still the expensive consequence. - ⬜ State §XVIII.6's headless invariant in the implementation.
- ⬜ §VIII.3 first bullet — where the sinking semaphore mechanically lives. The last open design question.
- ⬜ Item 16 — every message-holding teardown path reaches
STADIUM-EVICT; verify viafleet_conserved(§XV.4). - ⬜ Item 17 — Hera's suicide: replace
BYE's cold-restart, or a separate word (§XVI.2). - ⬜ Item 18 — the empty-floor halt when Hera is already gone (§XVI.7).
- ⬜ Category B strips, each as coding proves the item dead (§XXII.1, §XXII.4.4).
XXIII. §VIII.3 SETTLED: the sinking latch is a kernel-resident scalar, read through a registered primitive
The last open design question in this document. §VIII.1 ruled what the signal is and why it is not a message; this settles where it mechanically lives and who reads it.
XXIII.1 — What the mechanism has to satisfy
Collected from the rulings, because together they constrain the answer almost completely:
- Readable by a VM whose messaging is dying (§VIII.2) — so it cannot itself route.
- Below the routing layer (§III.2) — kernel-resident, in kernel-Hermes's own scope.
- Must not require Hermes to still be working — the whole point is that the transport is the thing that failed.
- Must not make the kernel a supervisor (§XV.3, §XIV.5) — the kernel may publish a fact; it may not decide another VM's fate.
- Must be actionable by a VM that has no thread of its own — per
FABRIC-3.md§XXVIII there is no per-VM native stack; every VM runs on the one shared kernel C stack and only executes when dispatched.
XXIII.2 — RULING: a single-writer kernel scalar, exposed as a registered primitive
The latch is a plain scalar in kernel-Hermes's own translation unit, written only by Hermes,
read by every VM through a primitive registered into its dictionary — e.g.
SINKING? ( -- flag ).
This invents nothing. The registration path is the one §XX of FABRIC-3.md already
established and fixed: register_child_vm_words() hands the eight STADIUM-* primitives to
every child VM, and Hera's two registration sites mirror them so her dictionary is a proper
superset. A ninth kernel-state reader is that same pattern, not a new mechanism — and it
satisfies (1) and (3) exactly, because reading a scalar needs no queue, no channel, no routing
table and no working arbiter.
Deliberately a plain unconditional primitive, not gated. Same doctrine CONSOLE-ATTACH and
ZUSE-ELIGIBILITY-ADD already carry (§XXXII.2 Q3): restricting who may call it, if that is
ever wanted, is ' SINKING? ACL-PIN in ACL.4th, never a bespoke C check. Reading a fact is
not an authority.
Keep it a bare scalar, not a field in a larger struct. Robustness, not style: the reader must get a coherent answer while the writer's own subsystem is failing. A standalone word-sized value cannot be observed mid-update; a field inside a structure being torn down can.
XXIII.3 — It is a one-way latch, not a counting semaphore — and that is what makes it safe
"Semaphore" was the word in the original framing; the accurate word is latch, and the distinction matters both for naming and for correctness.
It is monotonic. Per §VII.1, a VM attempts its own gentle recovery before anything is announced. So Hermes raising the latch means recovery has already failed and it is committed to going down — there is no path back to "not sinking." Raise once; never lower.
That gives a concurrency story with nothing in it:
- Single writer (Hermes, mainline), many readers, one irreversible transition. A reader either sees the old value or the new one, and both are valid — seeing "not sinking" one moment before the raise is indistinguishable from having read a moment earlier, which is fine.
- This is the discipline this codebase has already sanctioned twice, and reuses on purpose.
heartbeat.c:33-34states it verbatim: "Single writer (mainline, viavm_tick()'s Loop #7 site), single reader (the ISR's re-arm call) — no lock needed, per §21.1's finding."FABRIC-3.md§XXVIII Stage 3 then reused that same sanctioned pattern for a second variable rather than "inventing new locking or overturning the ruling itself." This is the third such variable, and it takes the same route for the same reason.
Naming it a semaphore in code would imply counting and blocking semantics it does not have and must not acquire. Call it what it is.
XXIII.4 — Who reads it, and when: the kernel delivers the fact, the VM performs the act
The subtle part, and the one constraint (5) forces. A VM cannot poll. With no per-VM thread (§XXVIII), a VM only runs when dispatched — so "every VM watches the latch" is not implementable as stated.
So the check belongs at the dispatch point, and the split of responsibility is the whole design:
On giving a VM its turn, the kernel checks the latch. If raised, that VM runs its own clean-shutdown routine —
blk_flush(0)thenBYE(§XVI) — in its own context, instead of its normal work.
- The kernel publishes a fact and delivers it. It does not shut anyone down, does not decide who dies, does not order anything. §XV.3 and §XIV.5 hold.
- Each VM performs its own shutdown, which is exactly what §IX.1 already ruled for fleet-wide shutdown ("the rest of the fleet runs its own shutdown routines") and §XVI defined the content of.
- No timer, no watcher, no supervisor. A VM acts when it next runs, which is the doctrine's own sanctioned "a VM's own internal state determining whether it does anything."
And the window closes by itself. Per §XVI.4, Hera's suicide bounds it: children flush and
BYE as they are dispatched, Hera reaps what remains and halts the processor. §VIII.3's "is
the window bounded?" needs no deadline because the last step is physically terminal — there is
nothing to time out.
XXIII.5 — The two-mechanism rule, now complete
§VIII.2 predicted the shape; with §XXI and §XXIII both settled it closes cleanly, with no exceptions list to maintain:
| Sender is… | Signal | Why |
|---|---|---|
| On the Stadium floor (Hera, Artemis, Hestia, identity VMs, agent VMs) | SOS, a routed message (§XXI) |
Routing is available to them; it travels up the birth graph to the one VM with authority to act |
| The arbiter beneath the floor (kernel-Hermes) | the sinking latch (§XXIII) | It cannot route a message announcing that routing has failed |
Which mechanism a VM uses is determined entirely by which side of the routing layer it sits on. Nothing is special-cased, and there is no list of exceptions to keep in sync — a property worth defending if a future component is ever tempted to want "just a small" third signal.
XXIII.6 — Punch list: the design phase is complete
Every design question this document opened is now ruled. What remains is build sequencing and three small local decisions:
- ⬜ SURGICAL STRIP — Category A (§XXII.6). Precedes everything.
- ⬜ Hestia: relocate
fabric.4th+font.4th; movePLOT/FB-WIDTH/FB-HEIGHT(§XVIII.9.1). - ⬜ Hestia's block range, avoiding 4997 (§XVIII.9.2).
- ⬜ Hestia into
is_fleet_foundation; birth atkernel_main.c:865; switch registration (§XIX.6). - ⬜
doe_log.cCSV schema (§XIII.3) — the expensive consequence. - ⬜ §XVIII.6's headless invariant stated in the implementation.
- ⬜ Item 16 — teardown paths reach
STADIUM-EVICT; verify viafleet_conserved(§XV.4). - ⬜ Item 17 — Hera's suicide: replace
BYE's cold-restart, or a separate word (§XVI.2). - ⬜ Item 18 — the empty-floor halt when Hera is already gone (§XVI.7).
- ⬜ Category B strips, each as coding proves the item dead (§XXII.4).
Items 8 and 9 are the only two carrying an unmade decision; the rest are execution. Nothing on this list is authorized by this document — per Captain Bob's Law, no code without an explicit instruction.