Multiuser DoE punch list: WORKER-BIRTH + VM-ERROR? + vm_physics_init fix
Core mechanism for §XXXIII's main concurrency block, built and live-verified on amd64. Two new primitives: - WORKER-BIRTH ( capsule-c capsule-u name-c name-u -- ok? ): births a named, VM-EXEC-addressable VM from an arbitrary (p) capsule with no identity involved. Corrects the ratified design's own assumption that the main block would use UNATTENDED-BIRTH -- that requires a committed capsule per identity, impractical for dozens of trial VMs. The concurrency block never needed identity at all. - VM-ERROR? ( c-addr u -- flag ): reads a named VM's error state from Hera, mirroring VM-HEAT's silent/always-returns-a-value contract. Needed to check a VM-EXEC-driven trial VM's own fault state after the fact -- nothing existing let Hera do this. Real bug found and fixed in both WORKER-BIRTH and UNATTENDED-BIRTH: capsule_birth_baby() never calls vm_physics_init() either (same shape as the registry-name gap found building UNATTENDED-BIRTH) -- without it a born VM is never in the VM Fleet Attractor physics list, so VM-HEAT returns 0 forever regardless of work done. Fixed by adding vm_physics_init() alongside the existing registry-name call in both words. Traced (not guessed) why heat still read 0 after one VM-EXEC touch even post-fix: vm_physics_touch()'s transfer logic only fires from a VM's *second* touch onward -- the first touch just records a baseline tick. Verified live across three sequential touches: heat 0 -> 7039 -> 11333. This is a real design requirement for the DoE's heat/CV response variable (each trial must touch a worker at least twice), not a bug to route around. Also found live: all 10 existing workload-N.4th capsules self-execute their full workload at load/birth time (a bare top-level call to their own RUN-* word at file end) -- missed on an earlier, too-shallow 8-line survey of each file. WORKER-BIRTH alone already runs a worker's first pass as a side effect of birth. Verified live on amd64: two concurrent workers (fib + matrix-mul) birthed, run, measured (heat + error state), and killed cleanly; VM-HEAT/VM-ERROR? both confirmed silent-0 on an unknown name. Clean 3-arch qemu boot on the real committed change. Still open: the run-matrix/shuffle/CSV driver capsule itself, the WIREBIND-automation path for the fixed arm, and the per-VM touch-count budget's exact value. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
bc2e294c50
commit
e33eb36361
@@ -1022,6 +1022,52 @@ static void mama_word_vm_heat(VM *vm)
|
||||
vm_push(vm, (cell_t)vm_physics_heat_of(entry.vm_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief VM-ERROR? ( c-addr u -- flag )
|
||||
* Push TRUE (-1) if the named VM currently has vm->error set, FALSE (0)
|
||||
* otherwise -- including if the name is unknown/dead (same "silent,
|
||||
* always returns a value" contract VM-HEAT above already established,
|
||||
* for the same reason: a caller checking many VM names after a
|
||||
* concurrent run shouldn't have to filter console noise for names that
|
||||
* are simply not currently live). Added 2026-09-16 for the multiuser/
|
||||
* multitasking DoE's correctness-proxy response variable (FABRIC-3.md
|
||||
* §XXXIII.2) -- reading a VM-EXEC-driven trial VM's own error state
|
||||
* from Hera after the run, with no existing word to do it.
|
||||
*/
|
||||
static void mama_word_vm_error_query(VM *vm)
|
||||
{
|
||||
char vm_name[VM_NAME_MAX];
|
||||
uint32_t i;
|
||||
cell_t vm_u, vm_caddr;
|
||||
const char *src;
|
||||
VMRegistryEntry entry;
|
||||
|
||||
if (vm->dsp < 1) { vm_push(vm, 0); return; }
|
||||
|
||||
vm_u = vm_pop(vm);
|
||||
vm_caddr = vm_pop(vm);
|
||||
|
||||
if (vm_u <= 0 || (uint32_t)vm_u >= VM_NAME_MAX) {
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
const uint8_t *p = vm_ptr(vm, (vaddr_t)vm_caddr);
|
||||
if (!p) { vm_push(vm, 0); return; }
|
||||
src = (const char *)p;
|
||||
}
|
||||
for (i = 0; i < (uint32_t)vm_u; i++) vm_name[i] = src[i];
|
||||
vm_name[vm_u] = '\0';
|
||||
|
||||
if (capsule_vm_find_by_name_nocase(vm_name, &entry) != 0 || !entry.vm_ptr) {
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
vm_push(vm, ((VM *)entry.vm_ptr)->error ? (cell_t)-1 : (cell_t)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief MINT ( -- ok? )
|
||||
* Mint a fresh identity onto the currently attached USB drive
|
||||
@@ -1614,6 +1660,105 @@ void mama_word_capsule_birth(VM *vm)
|
||||
vm_push(vm, (cell_t)new_vm_id.hi);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief WORKER-BIRTH ( capsule-c capsule-u name-c name-u -- ok? )
|
||||
* Birth a named, VM-EXEC-addressable VM from an arbitrary (p) capsule --
|
||||
* no identity involved at all. Built for the multiuser/multitasking
|
||||
* DoE's own main concurrency block (FABRIC-3.md §XXXIII, 2026-09-16):
|
||||
* that block needs many concurrently-live worker VMs running the
|
||||
* existing workload-N.4th capsules directly, and neither existing birth
|
||||
* word fits -- CAPSULE-BIRTH (above) never registers a discoverable
|
||||
* name at all (VM-EXEC couldn't address the result), and
|
||||
* UNATTENDED-BIRTH requires the capsule to define and the cert to
|
||||
* verify a real identity, which a plain workload capsule has no reason
|
||||
* to carry. This is UNATTENDED-BIRTH's own birth+naming steps with the
|
||||
* identity-verification layer removed, not a new mechanism:
|
||||
* capsule_birth_baby() unmodified, then capsule_vm_registry_set_name()
|
||||
* *and* vm_physics_init() -- both explicit steps every birth caller
|
||||
* must do itself (capsule_birth_baby() sets neither; plain BIRTH,
|
||||
* mama_word_birth() above, is the only caller that already does both
|
||||
* together). Missing vm_physics_init() specifically was found live
|
||||
* testing this word for the DoE: without it, vm_physics_heat_of()
|
||||
* silently returns 0 forever (the VM is never in the physics list to
|
||||
* look up, not gated behind Stadium admission as first suspected --
|
||||
* checked capsule_vm_physics.c directly rather than guessing), making
|
||||
* VM-HEAT worthless for every worker this word births.
|
||||
*/
|
||||
static void mama_word_worker_birth(VM *vm)
|
||||
{
|
||||
char capsule_name[VM_NAME_MAX];
|
||||
char worker_name[VM_NAME_MAX];
|
||||
cell_t u, caddr;
|
||||
uint32_t i;
|
||||
|
||||
if (vm->dsp < 3) {
|
||||
console_println("WORKER-BIRTH: expects S\" capsule\" S\" name\" WORKER-BIRTH");
|
||||
vm->error = 1;
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
u = vm_pop(vm);
|
||||
caddr = vm_pop(vm);
|
||||
if (u <= 0 || (uint32_t)u >= VM_NAME_MAX) {
|
||||
console_println("WORKER-BIRTH: name too long or empty");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
{
|
||||
const uint8_t *p = vm_ptr(vm, (vaddr_t)caddr);
|
||||
if (!p) {
|
||||
console_println("WORKER-BIRTH: invalid address on the stack");
|
||||
vm->error = 1;
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < (uint32_t)u; i++) worker_name[i] = (char)p[i];
|
||||
}
|
||||
worker_name[u] = '\0';
|
||||
|
||||
u = vm_pop(vm);
|
||||
caddr = vm_pop(vm);
|
||||
if (u <= 0 || (uint32_t)u >= VM_NAME_MAX) {
|
||||
console_println("WORKER-BIRTH: capsule name too long or empty");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
{
|
||||
const uint8_t *p = vm_ptr(vm, (vaddr_t)caddr);
|
||||
if (!p) {
|
||||
console_println("WORKER-BIRTH: invalid address on the stack");
|
||||
vm->error = 1;
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
for (i = 0; i < (uint32_t)u; i++) capsule_name[i] = (char)p[i];
|
||||
}
|
||||
capsule_name[u] = '\0';
|
||||
|
||||
VMUuid new_vm_id = vm_uuid_none();
|
||||
void *new_vm_ctx = (void *)0;
|
||||
CapsuleRunResult result = capsule_birth_baby(
|
||||
capsule_name,
|
||||
capsule_get_directory(),
|
||||
capsule_get_descriptors(),
|
||||
capsule_get_names(),
|
||||
capsule_get_arena(),
|
||||
vm->stadium_vm_id,
|
||||
0, /* skip_pki_sig: normal build-time capsule, same as CAPSULE-BIRTH */
|
||||
&new_vm_id,
|
||||
&new_vm_ctx
|
||||
);
|
||||
if (result != CAPSULE_RUN_OK || !new_vm_ctx) {
|
||||
console_println("WORKER-BIRTH: birth FAILED");
|
||||
vm_push(vm, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
capsule_vm_registry_set_name(new_vm_id, worker_name);
|
||||
vm_physics_init(new_vm_id);
|
||||
vm_push(vm, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief UNATTENDED-BIRTH ( capsule-c capsule-u name-c name-u -- ok? )
|
||||
* Birth an unattended identity from a named (p) capsule -- the
|
||||
@@ -1768,6 +1913,7 @@ static void mama_word_unattended_birth(VM *vm)
|
||||
}
|
||||
born_vm->identity = identity;
|
||||
capsule_vm_registry_set_name(new_vm_id, reg_name);
|
||||
vm_physics_init(new_vm_id);
|
||||
|
||||
console_println("UNATTENDED-BIRTH: identity installed, no console attached");
|
||||
vm_push(vm, 1);
|
||||
@@ -2225,6 +2371,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "MINT", mama_word_mint);
|
||||
register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch);
|
||||
register_word(vm, "MINT-SCRATCH-EMIT", mama_word_mint_scratch_emit);
|
||||
register_word(vm, "WORKER-BIRTH", mama_word_worker_birth);
|
||||
register_word(vm, "UNATTENDED-BIRTH", mama_word_unattended_birth);
|
||||
register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach);
|
||||
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
|
||||
@@ -2242,6 +2389,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "SWITCH-MARK-WORK", mama_word_switch_mark_work);
|
||||
register_word(vm, "VM-CALL", mama_word_vm_call);
|
||||
register_word(vm, "VM-HEAT", mama_word_vm_heat);
|
||||
register_word(vm, "VM-ERROR?", mama_word_vm_error_query);
|
||||
/* FABRIC-3.md SXX: the 8 STADIUM-* primitives register_child_vm_words()
|
||||
* gives every other VM, added here too -- root cause of "Hera cannot
|
||||
* load common:messaging.4th" (kernel_main.c's old Phase C comment):
|
||||
@@ -2288,6 +2436,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "MINT", mama_word_mint);
|
||||
register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch);
|
||||
register_word(vm, "MINT-SCRATCH-EMIT", mama_word_mint_scratch_emit);
|
||||
register_word(vm, "WORKER-BIRTH", mama_word_worker_birth);
|
||||
register_word(vm, "UNATTENDED-BIRTH", mama_word_unattended_birth);
|
||||
register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach);
|
||||
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add);
|
||||
@@ -2305,6 +2454,7 @@ void register_mama_forth_words(VM *vm)
|
||||
register_word(vm, "SWITCH-MARK-WORK", mama_word_switch_mark_work);
|
||||
register_word(vm, "VM-CALL", mama_word_vm_call);
|
||||
register_word(vm, "VM-HEAT", mama_word_vm_heat);
|
||||
register_word(vm, "VM-ERROR?", mama_word_vm_error_query);
|
||||
/* FABRIC-3.md SXX: the 8 STADIUM-* primitives register_child_vm_words()
|
||||
* gives every other VM, added here too -- root cause of "Hera cannot
|
||||
* load common:messaging.4th" (kernel_main.c's old Phase C comment):
|
||||
@@ -2565,6 +2715,7 @@ void register_child_vm_words(VM *vm)
|
||||
register_word(vm, "SWITCH-MARK-WORK", mama_word_switch_mark_work);
|
||||
register_word(vm, "VM-CALL", mama_word_vm_call);
|
||||
register_word(vm, "VM-HEAT", mama_word_vm_heat);
|
||||
register_word(vm, "VM-ERROR?", mama_word_vm_error_query);
|
||||
/* USE (FABRIC-2.md §F.24): not console-specific -- any VM can
|
||||
* redirect the physical REPL to any other VM it has ACL access to
|
||||
* (BINDSTEP re-verifies on every call, §F.9), including a console
|
||||
|
||||
Reference in New Issue
Block a user