Hera can't message: root-caused and fixed by symmetry (FABRIC-3.md §XX)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run

Hera never loaded common:messaging.4th, unlike every other VM in the
fleet. The standing belief was this was deliberate, to avoid moving
her dict_hash off baseline. Checked live instead of assumed: loading
messaging.4th into her dictionary silently dropped every colon-
definition referencing one of 8 STADIUM-* primitives that
register_child_vm_words() gives every other VM but
register_mama_forth_words() never gave her -- a missing-primitive gap,
not a designed privilege boundary. Confirmed mama_word_birth is
genuinely VM-agnostic and SPAWN-EVENT is an unwired placeholder before
proposing the fix.

Fix: register the same 8 STADIUM-* primitives for Hera, load
messaging.4th from init.4th the same way Hermes/Artemis/console/mint
already do, and give her own idle-loop context a direct MSG-TICK call
(not VM-EXEC, which would hit the same reentrancy class the existing
per-other-VM pump loop already guards against) so her own queued
messages actually drain. Her dictionary is now a proper superset of
every child VM's, plus her remaining extra privileges -- not
structurally different from any other VM, just additionally
privileged.

Verified: dict_hash identical across amd64/aarch64/riscv64
(0xc8f4b09e36f4fc4a), Hermes/Artemis dict_hashes unchanged and still
cross-arch identical, all three boot clean to zuse)ok> with zero
UNKNOWN WORD faults, mkcapsule --lint clean.

Unblocks rewriting the turn-attractor (FABRIC-3.md §XIX) to coordinate
via real MSG-SEND/MSG-TICK instead of blocking VM-EXEC.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo
This commit is contained in:
Robert Allan James
2026-09-12 16:05:11 -04:00
co-authored by Claude Sonnet 5
parent 5a9425b91b
commit 7edccd2c35
8 changed files with 255 additions and 62 deletions
+98
View File
@@ -2754,3 +2754,101 @@ from §XV. The actual heat-driven "coolest live identity goes next" turn loop, a
the whole campaign (including whether K still holds under genuinely interleaved multi-VM
contention, which is the actual point of building this) is the next step, not done here.
## XX. Hera can't message -- root-caused and fixed by symmetry, not special-cased (2026-09-12)
**Why this had to happen before the turn-attractor.** Building the §XIX turn-attractor around
`MSG-SEND`/`MSG-TICK` (the project's real async coordination layer, rather than blocking
`VM-EXEC`) requires Hera to be a genuine participant in that layer. She wasn't: Hera never
loaded `common:messaging.4th`, unlike Hermes, Artemis, and every identity VM. Before designing
around that gap, Captain Bob's framing was checked directly: "I don't want us to begin thinking
of Hera as being different than any other VM... the only real difference in Hera should be
access to the kernel capsule repository." That's a testable claim about the code, not just a
preference, so it was tested rather than assumed.
**Root cause, not a design boundary.** The standing belief (recorded in the old comment this
section replaces, `kernel_main.c`) was that Hera's messaging exclusion was deliberate --
"to avoid moving Hera's dict_hash off item 4.1's baseline." Checked live instead of trusted:
loading `common:messaging.4th` into Hera's own dictionary silently dropped every colon-definition
that referenced one of eight `STADIUM-*` primitives (`STADIUM-ADMIT`, `STADIUM-EVICT`,
`STADIUM-RES@`, `STADIUM-RES-PULL`, `STADIUM-RES-PUSH`, `STADIUM-HEAT@`, `STADIUM-HEAT!`,
`STADIUM-WORD-HEAT`) -- `register_child_vm_words()` gives every child VM these eight, but
`register_mama_forth_words()` never did. Referencing an undefined word during FORTH compilation
doesn't raise a hard error, it silently drops the definition being compiled -- so `MSG-HEAT@/!`,
`CH-HEAT@/!`, `MSG-COOL-ALL`, `MSG-TICK`, and everything else in `messaging.4th` that touches
Stadium came out missing after boot, with no error printed. Not a messaging bug and not a
designed privilege boundary -- a plain missing-primitive gap in Hera's own dictionary
specifically, that happened to surface as silently-vanished word definitions.
**Two things independently checked before proposing a fix, not guessed at:**
- `mama_word_birth` (`mama_forth_words.c:229`, `BIRTH`'s C implementation) was read in full.
It is genuinely VM-agnostic -- it operates on whichever `vm` calls 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()` treats `vm->stadium_vm_id`
generically as "whoever is birthing this VM." So "any VM could theoretically birth another" is
already true in the code, independent of this fix -- Hera's practical monopoly on `BIRTH` is
a registration-time fact (only `register_mama_forth_words()` registers it), not a check inside
`BIRTH` itself.
- `SPAWN-EVENT` (defined as the constant `1` in both `messaging.4th` and `process.4th`) was
grepped for every consumer across the tree. There are none -- it is never dispatched or
matched on anywhere. An unwired placeholder, not working infrastructure that this fix needed
to route through.
**The fix, scoped and approved before being built ("scope it, plan it and after approval do
it"):** register the same eight `STADIUM-*` primitives for Hera that `register_child_vm_words()`
already gives every other VM -- in both of Hera's own registration sites
(`register_mama_forth_words()`'s root vocabulary and her MAMA vocabulary), matching
`register_child_vm_words()`'s list exactly. Then load `common:messaging.4th` from
`capsules/init.4th` (block 2049) the same way Hermes/Artemis/console/mint already do, and call
`MSG-CD-INIT`. This makes Hera's dictionary a proper *superset* of every child VM's, plus her
own remaining extra privileges (`BIRTH`, the capsule-repository words, `MINT`) -- not
structurally different from any other VM, just additionally privileged on top of an otherwise
identical base. Fixed by symmetry, exactly as scoped, no special-casing introduced.
**A necessary follow-on, caught while updating the stale comment rather than just rewriting the
text.** `repl.c`'s idle-loop pump already walked the VM registry every idle beat, `VM-EXEC`ing
`"MSG-TICK"` into every *other* live VM to drain their queues -- explicitly skipping Hera's own
registry entry. Re-reading why (not assumed): the skip exists because Hera *is* the pump, and
self-targeting `VM-EXEC` would hit the same reentrancy class the loop's own dispatch guard
exists for (the same class documented elsewhere for `input_buffer`/`input_pos` not being saved
by `vm_state_push`/`pop`) -- not because she lacked `MSG-TICK`. With messaging now loaded for
her, that skip would silently leave her own queued messages undrained forever if left alone. Not
requested explicitly, but a necessary completion of "Hera is a genuine peer" -- if her own
messages never get pumped, she isn't actually participating in the layer, just able to compile
against it. Fixed by calling `MSG-TICK` directly in her own context (a plain word dispatch in
her own dictionary, not a `VM-EXEC` into anyone else's input buffer -- no reentrancy risk),
guarded the same defensive way as the loop above it (`vm_find_word` + `acl_allow` checked fresh
every tick, not a cached flag).
**Verification: dict_hash parity held across all three architectures**, the property that
actually matters (identical value across amd64/aarch64/riscv64, not an unchanging absolute
value) -- confirmed identical post-fix on all three: `dict_hash=0xc8f4b09e36f4fc4a`,
`capsule_hash=0x1ef4939ed32ec1e6`. Hermes' and Artemis' own dict_hashes
(`0x0046d7d93aa33935`, `0x0e9e13b9c3a08542`) also matched across all three architectures,
confirming the larger, now-symmetric dictionary didn't perturb child-VM parity either. All
three booted clean to `zuse)ok>` with zero `UNKNOWN WORD` faults. `mkcapsule --lint capsules/`
passes clean (0 violations) after the `messaging.4th`/`init.4th` block-namespace edits this
required.
**Files touched:** `src/starkernel/capsule/mama_forth_words.c` (8 `STADIUM-*` registrations +
forward declarations, in both Hera registration sites), `capsules/init.4th` (block 2049, load
`common:messaging.4th` + `MSG-CD-INIT`), `capsules/common/messaging.4th` (VM routing table
widened 8→16 slots for the identity VMs, block 5006/5042, `mkcapsule --lint` clean),
`src/starkernel/repl.c` (direct `MSG-TICK` call for Hera's own context after the existing
per-other-VM pump loop), `src/starkernel/kernel_main.c` (stale Phase C comment corrected to
describe the fix instead of the old, now-disproven constraint).
**Explicitly captured for later, not in scope here:** 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 floated during this discussion, deliberately
not built as part of this fix.
**Next step, now unblocked:** rewrite `EXEC-STD79-DOE-CD` (the draft turn-attractor sketched in
§XIX, written *before* this pivot and not yet reworked) to coordinate via `MSG-SEND`/`MSG-TICK`
rather than direct blocking `VM-EXEC`, add a `TIE-BREAK ( idx1 idx2 -- idx )` word (seeded coin
toss via `RANDOM` to start, swappable for inference later), change turn priority from raw
`VM-HEAT` to `VM-HEAT / tests-remaining`, confirm the natural "sender pays" reservoir economy
(`STADIUM-RES-PULL`/`STADIUM-RES-PUSH` operate on the *calling* VM's own reservoir, confirmed
via `mama_forth_words.c` -- turn cost falls on whichever VM sends, not a "winner pays" model),
then rerun the full 3×9×3 DoE campaign from the top per the standing rule that any defect
repair requires a clean re-run before a result counts as closed.