Multiuser DoE punch list: WORKER-BIRTH + VM-ERROR? + vm_physics_init fix
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run

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:
Robert Allan James
2026-09-16 08:51:21 -04:00
co-authored by Claude Sonnet 5
parent bc2e294c50
commit e33eb36361
20 changed files with 73938 additions and 1 deletions
+55
View File
@@ -4936,3 +4936,58 @@ proven at this scale.
standing convention, at least one full pass reviewed for plausibility before calling the standing convention, at least one full pass reviewed for plausibility before calling the
harness itself proven. harness itself proven.
### XXXIII.4 -- Punch list progress, 2026-09-16: core mechanism built and live-verified
**A scoping correction found immediately on starting implementation:** the ratified design's
main block said "every concurrent VM is `UNATTENDED-BIRTH`'d." Wrong in practice --
`UNATTENDED-BIRTH` requires its own committed capsule per identity (mint, hand-transcribe,
author a `.4th` file), completely impractical to repeat for dozens of trial VMs across a real
factorial. The main concurrency block never needed identity at all -- it's stressing
concurrency and workload execution, not identity verification (already proven in §XXXII).
**New primitive: `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 at
all, built specifically for this main block. `UNATTENDED-BIRTH` stays exactly as built, for
the fixed arm where identity origin is the thing being varied.
**A second gap found the same way UNATTENDED-BIRTH's registry-name gap was found:**
`capsule_birth_baby()` also never calls `vm_physics_init()` -- so a `WORKER-BIRTH`'d (and, it
turns out, an `UNATTENDED-BIRTH`'d) VM was never registered in the VM Fleet Attractor physics
list at all, making `VM-HEAT` return 0 forever regardless of how much work the VM did. Fixed
in both words (`vm_physics_init(new_vm_id)` alongside the existing
`capsule_vm_registry_set_name()` call) -- plain `BIRTH` was the only existing caller doing
both together, another instance of "every birth caller must do these steps itself,
`capsule_birth_baby()` sets neither."
**A real design requirement for the heat/CV response variable, traced to ground truth in
`vm_physics_touch()` rather than guessed at:** heat only transfers from a VM's *second* touch
onward -- the first touch on any VM only records a baseline tick and sets `touched=1`, moving
zero heat by construction (`capsule_vm_physics.c:301-303`'s own gating condition). Verified
live: heat read `0` after one `VM-EXEC` touch, `7039` after a second, `11333` after a third.
**Design requirement, not a bug to route around:** each trial's per-worker driving sequence
must call `VM-EXEC` at least twice for heat to be a meaningful signal at all.
**New primitive: `VM-ERROR? ( c-addr u -- flag )`** -- reads a named VM's `vm->error` state
from Hera, mirroring `VM-HEAT`'s exact "silent, always returns a value" contract (0 on unknown/
dead name, no error). Needed because nothing previously let Hera check a `VM-EXEC`-driven
trial VM's own fault state after the fact.
**Live-verified end to end on amd64:** birthed two concurrent workers (`w1` from
`workload-8.4th`/fib, `w2` from `workload-7.4th`/matrix-mul) via `WORKER-BIRTH`, confirmed both
alive and running distinct real workloads simultaneously, read heat and error state on both
after driving real work through `VM-EXEC`, confirmed `VM-HEAT`/`VM-ERROR?` both return silent 0
(no crash) for a nonexistent name, and `KILL`'d both cleanly. Separately verified the
heat-transfer mechanism itself across three sequential touches on one worker. Hera stayed
healthy throughout every test (`5 6 + .` -> `11` after each). Clean 3-arch qemu boot on the
real committed change.
**Also found, not chased:** every one of the 10 existing `workload-N.4th` capsules
self-executes its full workload immediately at load/birth time (a bare top-level call to its
own `RUN-*` word at the end of the file) -- confirmed by reading each file's actual tail, not
assumed from its first ~10 lines as an earlier pass of this investigation did. This means
`WORKER-BIRTH` alone already runs a worker's assigned workload once as a side effect of birth;
no separate "load then call" step is needed for that first run, only for subsequent
`VM-EXEC`-driven touches.
**Still not started:** the run-matrix/shuffle/CSV driver itself, the WIREBIND-automation path
for the fixed arm, and the per-VM iteration/touch-count budget's exact value.
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated # Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-09-16T11:22:44Z --> <!-- Generated by mkcapsule --manifest 2026-09-16T12:48:45Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. --> <!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live --> <!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. --> <!-- in MANIFEST.md alongside this auto-generated index. -->
BIN
View File
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+151
View File
@@ -1022,6 +1022,52 @@ static void mama_word_vm_heat(VM *vm)
vm_push(vm, (cell_t)vm_physics_heat_of(entry.vm_id)); 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? ) * @brief MINT ( -- ok? )
* Mint a fresh identity onto the currently attached USB drive * 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); 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? ) * @brief UNATTENDED-BIRTH ( capsule-c capsule-u name-c name-u -- ok? )
* Birth an unattended identity from a named (p) capsule -- the * 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; born_vm->identity = identity;
capsule_vm_registry_set_name(new_vm_id, reg_name); 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"); console_println("UNATTENDED-BIRTH: identity installed, no console attached");
vm_push(vm, 1); 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", mama_word_mint);
register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch); register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch);
register_word(vm, "MINT-SCRATCH-EMIT", mama_word_mint_scratch_emit); 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, "UNATTENDED-BIRTH", mama_word_unattended_birth);
register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach); register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach);
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add); 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, "SWITCH-MARK-WORK", mama_word_switch_mark_work);
register_word(vm, "VM-CALL", mama_word_vm_call); register_word(vm, "VM-CALL", mama_word_vm_call);
register_word(vm, "VM-HEAT", mama_word_vm_heat); 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() /* FABRIC-3.md SXX: the 8 STADIUM-* primitives register_child_vm_words()
* gives every other VM, added here too -- root cause of "Hera cannot * gives every other VM, added here too -- root cause of "Hera cannot
* load common:messaging.4th" (kernel_main.c's old Phase C comment): * 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", mama_word_mint);
register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch); register_word(vm, "MINT-SCRATCH", mama_word_mint_scratch);
register_word(vm, "MINT-SCRATCH-EMIT", mama_word_mint_scratch_emit); 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, "UNATTENDED-BIRTH", mama_word_unattended_birth);
register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach); register_word(vm, "CONSOLE-ATTACH", mama_word_console_attach);
register_word(vm, "ZUSE-ELIGIBILITY-ADD", mama_word_zuse_eligibility_add); 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, "SWITCH-MARK-WORK", mama_word_switch_mark_work);
register_word(vm, "VM-CALL", mama_word_vm_call); register_word(vm, "VM-CALL", mama_word_vm_call);
register_word(vm, "VM-HEAT", mama_word_vm_heat); 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() /* FABRIC-3.md SXX: the 8 STADIUM-* primitives register_child_vm_words()
* gives every other VM, added here too -- root cause of "Hera cannot * gives every other VM, added here too -- root cause of "Hera cannot
* load common:messaging.4th" (kernel_main.c's old Phase C comment): * 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, "SWITCH-MARK-WORK", mama_word_switch_mark_work);
register_word(vm, "VM-CALL", mama_word_vm_call); register_word(vm, "VM-CALL", mama_word_vm_call);
register_word(vm, "VM-HEAT", mama_word_vm_heat); 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 /* 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 * redirect the physical REPL to any other VM it has ACL access to
* (BINDSTEP re-verifies on every call, §F.9), including a console * (BINDSTEP re-verifies on every call, §F.9), including a console