diff --git a/FABRIC-3.5.md b/FABRIC-3.5.md index b38413d5..d7bedc17 100644 --- a/FABRIC-3.5.md +++ b/FABRIC-3.5.md @@ -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.