Hera can't message: root-caused and fixed by symmetry (FABRIC-3.md §XX)
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:
co-authored by
Claude Sonnet 5
parent
5a9425b91b
commit
7edccd2c35
+98
@@ -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.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user