Phase C: distributed messaging capsule + idle-loop pump

Extract the messaging vocabulary (arenas, MSG-*/CH-*/MBR-* words) out of
capsules/hermes/init.4th into a new shared capsules/common/messaging.4th
that Hermes and Artemis each load at birth, giving every VM its own
private MSG-ARENA/CH-ARENA instead of only Hermes having one. Hermes
stays the owner of the one real, canonical COMMON-CH; Artemis subscribes
into it via VM-EXEC at her own birth, and Hermes proactively subscribes
Hera (idx 0) since Hera always exists first.

Hera does NOT get her own copy: register_child_vm_words()'s own doc
comment explains why the STADIUM-* primitives common:messaging.4th
depends on are deliberately never registered in her dictionary (keeps
her dict_hash off item 4.1's baseline). Confirmed live by loading it
into her dictionary anyway first -- every colon-definition referencing
an unregistered Stadium primitive was silently dropped (MSG-HEAT@/!,
CH-HEAT@/!, MSG-COOL-ALL, MSG-TICK all missing after boot). Reverted
that path; she orchestrates via BIRTH/VM-EXEC/VM-CALL instead.

Added capsule_vm_registry_get_by_index() (capsule_birth.c/.h) for
registry enumeration by birth-order position, and a pump in repl.c's
existing idle hook that walks every live VM once per idle beat and
VM-EXECs MSG-TICK into each one except Hera's own entry.

Verified clean (no UNKNOWN WORD / VM-EXEC errors after birth) on all
three architectures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
This commit is contained in:
Robert Allan James
2026-08-28 13:02:23 -04:00
co-authored by Claude Sonnet 5
parent 0ec91b517a
commit 21bca315ff
16 changed files with 56059 additions and 550 deletions
+12
View File
@@ -199,6 +199,18 @@ uint32_t capsule_vm_registry_count(void) {
return vm_registry_count;
}
int capsule_vm_registry_get_by_index(uint32_t index, VMRegistryEntry *out) {
vm_node_t *node = vm_registry_head;
uint32_t i = 0;
if (!out) return -1;
while (node) {
if (i == index) { *out = node->entry; return 0; }
node = node->next;
i++;
}
return -1;
}
/* Live population, distinct from vm_registry_count above: vm_registry_count
* is monotonic (incremented on every vm_registry_alloc(), never decremented
* on death), so it counts every VM ever born, not the outer Stadium's
+14
View File
@@ -813,6 +813,20 @@ static void kernel_main_deep(BootInfo *boot_info) {
}
}
/* Phase C (2026-08-28): Hera does NOT get her own common:messaging.4th
* arena -- register_child_vm_words()'s own doc comment (mama_forth_
* words.c) explains why: the eight STADIUM-* FORTH primitives that
* word depends on (CH-HEAT@/!, MSG-HEAT@/! etc. all route through
* them) are deliberately never added to register_mama_forth_words(),
* to avoid moving Hera's dict_hash off item 4.1's baseline. Confirmed
* live: loading messaging.4th into Hera's own dictionary silently
* dropped every colon-definition that referenced one of those
* primitives (MSG-HEAT@/!, CH-HEAT@/!, MSG-COOL-ALL, MSG-TICK all
* missing after boot) -- not a timing bug, a real, pre-existing
* constraint. Hera orchestrates via BIRTH/VM-EXEC/VM-CALL directly
* instead; the idle-loop pump (repl.c) skips her own registry entry
* for the same reason. */
/* Hermes is now a permanent fleet-foundation VM, not self-test
* scaffolding -- FABRIC-3.md D.7 (birth-by-message-only, 2026-08-28):
* the Tripod legs (Hera/Hermes/Artemis) must be alive session-less so
+41
View File
@@ -37,6 +37,9 @@
#include "starkernel/xhci_driver.h"
#include "starkernel/blkio_usb.h"
#include "starkernel/homeblocks_sig.h"
#include "starkernel/capsule_birth.h"
#include "starkernel/capsule_run.h"
#include "starkernel/vm/bootstrap/sk_vm_bootstrap.h"
#include "block_subsystem.h"
#include "word_source/include/keyboard_words.h"
#include "word_source/include/block_words.h"
@@ -185,6 +188,44 @@ static void sk_repl_idle(VM *active_vm)
* write with no UPDATE, followed by an idle wait and an abrupt kill,
* did not survive a reboot until this fix. */
blk_vm_flush_all(active_vm);
/* FABRIC-3.md Phase C (2026-08-28): distributed messaging pump. Every
* live VM except Hera herself now owns its own MSG-ARENA/CH-ARENA and
* MSG-TICK word (see capsules/common/messaging.4th) instead of only
* Hermes having one -- "fully distributed, Hera pumps each VM's
* drain" was the confirmed design. Hera is excluded: she never loads
* common:messaging.4th (kernel_main.c's own comment at the Hermes-
* birth call site explains why -- the STADIUM-* primitives it needs
* are deliberately never registered in her own dictionary, to keep
* her dict_hash off item 4.1's baseline), so MSG-TICK is genuinely
* absent there, not just untried. She is the only VM with a
* persistent idle tick, so she walks the registry once per idle beat
* and VM-EXECs MSG-TICK into every OTHER live VM's own dictionary. */
{
VM *mama = (VM *)sk_get_mama_vm();
uint32_t count = capsule_vm_registry_count();
uint32_t i;
for (i = 0; i < count; i++) {
VMRegistryEntry ent;
if (capsule_vm_registry_get_by_index(i, &ent) != 0) continue;
if (ent.state != VM_STATE_LIVE) continue;
if (ent.vm_ptr == (void *)mama) continue;
static const char PFX[] = "S\" MSG-TICK\" S\" ";
static const char SFX[] = "\" VM-EXEC";
char cmd[128];
size_t p = 0;
size_t name_len = strlen(ent.name);
if (name_len > VM_NAME_MAX - 1u) name_len = VM_NAME_MAX - 1u;
memcpy(cmd + p, PFX, sizeof(PFX) - 1u); p += sizeof(PFX) - 1u;
memcpy(cmd + p, ent.name, name_len); p += name_len;
memcpy(cmd + p, SFX, sizeof(SFX) - 1u); p += sizeof(SFX) - 1u;
cmd[p] = '\0';
vm_interpret(mama, cmd);
}
}
}
/*===========================================================================