FABRIC-3.5.md §XVI: item 10 settled -- clean shutdown is forced flush + BYE, Hera's is suicide

Captain Bob, 2026-09-18. A clean shutdown is a VM termination where flush
is forced and the VM says BYE; Hera is the special case that halts the
processor instead.

Traced, and the ruling is almost entirely existing mechanism.
blk_flush(0) is already the documented flush-all sentinel
(block_subsystem.c:988-990) and already the Stadium's own write-back
action. A child's BYE already terminates it -- system_word_bye sets
vm->halted, which mama_forth_words.c's own doc comment describes as the
child path. arch_halt() exists on all three architectures
(amd64:207, aarch64:126, riscv64:170), so three-arch parity holds
without new per-arch work.

The one real finding is a behavioural change rather than a gap: Hera's
BYE does not halt today, it cold-restarts. mama_word_bye() reaps children
then calls arch_cold_reset(). The reaping half already matches the
ruling; the terminal action is the opposite of it. Flagged so the change
is made knowingly -- CLAUDE.md forbids modifying a registered tested word
to "fix" it, and this is a ruled change rather than a fix, but it should
not surface first in a diff. Whether suicide replaces BYE or becomes a
separate word is left open as item 17, noting cold-restart-on-BYE is
plausibly wanted for an operator typing BYE at Hera's REPL.

Records a constraint on where the flush goes: system_word_bye lives in
shared vendored source that must keep working in the hosted build, so the
forced flush belongs in the kernel-side shutdown path, not inside BYE.

§VIII.3's bounded-window question answers itself -- the window ends when
Hera halts, a bound by construction with no timeout to tune, which is
consistent with §XV.3's no-owner invariant. Item 9 is now half-answered.

Raises one residual edge as analysis only: if Hera is already dead,
nobody performs the halt, leaving a live kernel over an empty floor. An
empty floor is arguably a kernel-observable condition rather than a VM's
decision, which would terminate without any VM inheriting her authority.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
This commit is contained in:
Claude
2026-09-18 22:01:00 +00:00
parent a842789ae6
commit bd0c23286c
+154 -6
View File
@@ -487,6 +487,12 @@ a semaphore. That is a clean rule with no exceptions list to maintain.
### VIII.3 — Open
> **PARTLY SETTLED 2026-09-18 by §XVI.** The second bullet (what a clean-shutdown routine does)
> is answered: forced `blk_flush(0)`, then `BYE`; Hera's is suicide via a permanent
> `arch_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.
@@ -596,15 +602,20 @@ Collected so nothing here is mistaken for settled. **Updated 2026-09-18 after §
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: four items.**
- **Item 10 (§VIII.3)** — the sinking semaphore's mechanism, what a clean-shutdown routine
concretely does, and whether the window is bounded. **The largest remaining design item.**
- **Item 9 (§VII.2)** — `SOS`'s message-type number and its consumer. Downstream of item 10,
and narrowed by §XV.2: with no death-declarer, an `SOS` consumer cannot be a supervisor.
**Still genuinely open — updated again 2026-09-18 after §XVI settled item 10.**
- **Item 12 (§XIII.5)** — is the Console Tripod leg a new singleton, distinct from today's
per-attach proxies? Independent of the others, and still gates §IV.
per-attach proxies? **Now the largest remaining design item**, and still gates §IV.
- **Item 9 (§VII.2/§XVI.5)** — half-answered. The consumer action is defined for the two
fleet-fatal cases; open is whether an *ordinary peer's* `SOS` is actionable or merely
advisory, plus the trivial number allocation.
- **§VIII.3 first bullet** — where the sinking semaphore mechanically lives. The rest of
§VIII.3 is settled by §XVI.
- **Item 16 (§XV.4)** — an implementation check rather than a decision: every message-holding
teardown path must reach `STADIUM-EVICT`, verifiable via `fleet_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 Console 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.
@@ -1116,3 +1127,140 @@ death, turn order, conservation — is physics rather than policy.
**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 at
`block_subsystem.c:849`, and already the Stadium's own write-back action for
`STADIUM_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`):
sets `vm->halted = 1`, signalling the REPL to stop. That is already precisely "a VM
termination." Confirmed by `mama_forth_words.c:2262-2263`'s own doc comment: "In child VMs
this word is never registered; children use the standard `system_word_bye` which sets
`vm->halted` and returns to the parent's REPL."
- **Halting the processor — `arch_halt()`**, declared at `include/starkernel/arch.h:64` and
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:
```c
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:339` describes
it as called "from Hera's BYE immediately before `arch_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 permanent `arch_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 `SOS` is actionable or advisory, plus the number.
- ⬜ **Item 12 (§XIII.5)** — Console 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.