Stage E: every born VM is a switch participant (FABRIC-3.8 §3.6, G4)
capsule_birth_baby() registers each VM at its LIVE completion point, beside the common-channel subscription. register() is idempotent. The Artemis/ Hestia registrations in kernel_main.c and WIREBIND's own call are removed (Hera, never born through capsule_birth_baby, stays with set_current). Probe (restored away): Tripod registered once each (3 slots), a WORKER-BIRTH VM becomes a participant with no hand registration (4) and KILL frees it (3), WIREBIND registers exactly console+user, zero duplicates, on amd64/aarch64/riscv64. Acceptance: dict_hash unchanged, relay 3, idle growth 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
604ad7aadc
commit
c7185facf0
@@ -1052,3 +1052,54 @@ explicit instruction.
|
||||
- WIREBIND's detach path calls `capsule_vm_kill()` directly, without the `KILL` word's
|
||||
`vm_physics_retire()`. I have not checked whether a detached identity's VM-physics heat
|
||||
is retired elsewhere.
|
||||
- [x] **Stage E — every born VM is a switch participant (§3.6, G4).** 2026-09-23. Captain Bob:
|
||||
*"yes"* (go ahead with Stage E).
|
||||
|
||||
Changes:
|
||||
- `capsule_birth.c`: `capsule_birth_baby()` registers the new VM right after its task 3.2
|
||||
common-channel subscription, at its LIVE completion point. The capsule code has already
|
||||
run to completion by then. A full table is logged and doesn't fail the birth.
|
||||
- `capsule_vm_switch_signal.c/.h`: `sk_vm_switch_signal_register()` is now
|
||||
**idempotent**. An already-registered VM gets its existing slot back, not a duplicate.
|
||||
- `kernel_main.c`: the Artemis/Hestia registrations are removed. Hera is still registered
|
||||
there (she's the one VM never born through `capsule_birth_baby()`), together with
|
||||
`sk_vm_switch_set_current(mama)`. Nothing can switch before that: every switch point
|
||||
requires a current VM.
|
||||
- `capsule_wirebind.c`: the user VM's `sk_vm_switch_signal_register()` call becomes a
|
||||
participation lookup. `switch_slot` was only ever used as a ≥ 0 flag and is now
|
||||
documented as one, since slot indices shift when the table compacts.
|
||||
- The WIREBIND console VM is now a participant too. That's harmless: it never has mail,
|
||||
so it is never chosen. It is unregistered when it's killed at detach (Stage D).
|
||||
- Registration order changes: Hestia, then Artemis at their births, then Hera. Decisions
|
||||
don't depend on order (C-b's walk-order evidence), but the DoE's `switch_current_slot`
|
||||
column now numbers them in that order.
|
||||
- Boot order checked: `sk_vm_switch_signal_boot_init()` (`kernel_main.c:536`) runs before
|
||||
the Tripod births (`:1124`/`:1142`).
|
||||
|
||||
**Evidence, as named in §4.** A temporary probe, restored away from a pre-probe snapshot
|
||||
and verified free of probe code:
|
||||
- `register()` prints every call, and flags any `DUPLICATE`;
|
||||
- `kernel_main.c` checks Tripod participation, then `WORKER-BIRTH`s `eprobe` **without
|
||||
registering it by hand**, checks it is a participant, and `KILL`s it.
|
||||
|
||||
Probe runs with the full relay: `logs/20260923-192830/amd64/`, `-193439/aarch64/`,
|
||||
`-194103/riscv64/`. Identical on all three:
|
||||
- `register` lines for Hestia (count 0 → 1), Artemis (1 → 2), Hera (2 → 3), eprobe
|
||||
(3 → 4);
|
||||
- `STAGE-E PROBE: tripod hera=1 artemis=1 hestia=1 slots=3; WORKER-BIRTH eprobe
|
||||
participant=1 slots=4; after KILL slots=3`, **PASS**;
|
||||
- at WIREBIND attach, exactly **two** registrations (the console VM and `rajames~user`);
|
||||
- **zero `DUPLICATE`** lines anywhere;
|
||||
- the relay still gives `3`.
|
||||
|
||||
**Formal acceptance with the probe gone:** `logs/20260923-194803/amd64/`,
|
||||
`-195401/aarch64/`, `-200026/riscv64/`.
|
||||
- All reach `[zuse@Hera] ok>` with zero `UNKNOWN WORD`, failures and faults.
|
||||
- The `dict_hash` set is unchanged.
|
||||
- The relay gives `3` through a real switch.
|
||||
- Idle log growth: 0 bytes.
|
||||
- Clean build on all three plus hosted, no new warnings.
|
||||
|
||||
**What this makes possible (not exercised yet):** every `WORKER-BIRTH`ed DoE worker is now
|
||||
a participant. Mail sent to a worker is delivered by a switch onto its own stack, and it
|
||||
competes by heat. Stage G's `SEND` word and interleave test are next.
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-23T23:07:10Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-23T23:59:50Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
|
||||
@@ -85,7 +85,10 @@ int sk_vm_switch_signal_boot_init(void);
|
||||
* or -1 if the slot table is full (or sk_vm_switch_signal_boot_init() was
|
||||
* never called / failed). Call once per participating VM, after that VM is
|
||||
* fully born (never mid-birth -- this stage has no critical-section
|
||||
* protection against being switched away mid-setup). */
|
||||
* protection against being switched away mid-setup). FABRIC-3.8.md §3.6
|
||||
* (Stage E): capsule_birth_baby() is now the one caller for every born VM
|
||||
* (Hera, never born through it, is registered in kernel_main.c).
|
||||
* Idempotent: registering an already-registered VM returns its slot. */
|
||||
int sk_vm_switch_signal_register(VMUuid vm_id);
|
||||
|
||||
/* Remove a switch-signal participant (FABRIC-3.md §XXVIII Stage 4,
|
||||
|
||||
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
@@ -920,6 +920,20 @@ CapsuleRunResult capsule_birth_baby(
|
||||
* and does not fail the birth itself. */
|
||||
(void)sk_hermes_channel_subscribe(SK_HERMES_CHANNEL_COMMON, vm_id);
|
||||
|
||||
/* FABRIC-3.8.md §3.6 (Stage E, G4): every born VM is a switch
|
||||
* participant -- same choke point, same "only once birth has fully
|
||||
* completed" rule as the subscription above (its capsule code has
|
||||
* already run to completion on the birthing VM's stack). BIRTH,
|
||||
* WORKER-BIRTH, UNATTENDED-BIRTH and WIREBIND identities all pass
|
||||
* through here, so none of them registers on its own any more. Soft
|
||||
* failure, same posture: a full table leaves this VM runnable only by
|
||||
* VM-EXEC, not a failed birth. A VM with no native stack is registered
|
||||
* but never chosen (sk_vm_switch_decide() skips it). */
|
||||
if (sk_vm_switch_signal_register(vm_id) < 0) {
|
||||
log_message(LOG_WARN, "capsule_birth_baby: switch-signal table full -- %s won't be switched to",
|
||||
capsule_name);
|
||||
}
|
||||
|
||||
capsule_parity_log_birth(vm_id, cap->capsule_id, cap->content_hash, dict_hash);
|
||||
|
||||
/* §H.12 step 11: applied AFTER dict_hash/parity logging above, not
|
||||
|
||||
@@ -143,6 +143,11 @@ int sk_vm_switch_signal_boot_init(void) {
|
||||
}
|
||||
|
||||
int sk_vm_switch_signal_register(VMUuid vm_id) {
|
||||
/* FABRIC-3.8.md §3.6 (Stage E): idempotent. Registration now happens
|
||||
* once, at birth; a second call returns the existing slot rather than
|
||||
* allocating a duplicate that the decision would walk twice. */
|
||||
int existing = slot_for_vm_id(vm_id);
|
||||
if (existing >= 0) return existing;
|
||||
if (g_slot_count >= g_slot_capacity) return -1;
|
||||
g_slots[g_slot_count].vm_id = vm_id;
|
||||
g_slots[g_slot_count].parked_stamp = 0;
|
||||
|
||||
@@ -80,9 +80,11 @@ typedef struct {
|
||||
struct blkio_dev *dev;
|
||||
VMUuid user_vm_id;
|
||||
char username[USER_IDENTITY_USERNAME_MAX];
|
||||
int switch_slot; /* Stage 4: index into the switch-signal
|
||||
* table, or -1 if not yet registered
|
||||
* (wired in the next increment). */
|
||||
int switch_slot; /* Stage 4: >= 0 if the user VM is a
|
||||
* switch-signal participant, -1 if not
|
||||
* (table full). FABRIC-3.8.md Stage E:
|
||||
* a flag, not a slot index -- indices
|
||||
* shift when the table compacts. */
|
||||
} wirebind_live_entry_t;
|
||||
|
||||
static wirebind_live_entry_t g_wirebind_live[WIREBIND_MAX_LIVE];
|
||||
@@ -313,7 +315,12 @@ void capsule_wirebind_try_attach(struct blkio_dev *dev,
|
||||
* failed attach -- the identity is still fully usable via
|
||||
* ordinary VM-EXEC/console-relay dispatch, same as before
|
||||
* Stage 4 existed. */
|
||||
live->switch_slot = sk_vm_switch_signal_register(user_id);
|
||||
/* FABRIC-3.8.md §3.6 (Stage E): no longer registered here --
|
||||
* capsule_birth_baby() registered the user VM (and, now, the
|
||||
* console VM too: harmless, it never has mail, so it is never
|
||||
* chosen) at birth. This only records whether that succeeded,
|
||||
* which is all switch_slot has ever been used for (>= 0). */
|
||||
live->switch_slot = sk_vm_switch_signal_is_participant(user_id) ? 0 : -1;
|
||||
memcpy(live->username, username, sizeof(live->username));
|
||||
if (live->switch_slot < 0) {
|
||||
console_println("WIREBIND: switch-signal table full -- this identity won't be preempted");
|
||||
|
||||
@@ -1255,8 +1255,15 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
* Artemis's own birth sequencing. FABRIC-3.6.md Phase 4 (Stage E),
|
||||
* 2026-09-22: Hermes's own registration here is retired along with
|
||||
* her birth above. */
|
||||
/* FABRIC-3.8.md §3.6 (Stage E, 2026-09-23): Artemis and Hestia are no
|
||||
* longer registered here -- capsule_birth_baby() registers every born
|
||||
* VM at its own LIVE completion point, which they already passed. Hera
|
||||
* is the one VM never born through it, so she is still registered
|
||||
* here, and this is still where the switch mechanism learns she is the
|
||||
* one running. Nothing can switch before sk_vm_switch_set_current():
|
||||
* every switch point requires a current VM. */
|
||||
{
|
||||
VMRegistryEntry hera_entry, artemis_entry;
|
||||
VMRegistryEntry hera_entry;
|
||||
if (capsule_vm_registry_get(vm_uuid_hera(), &hera_entry) == 0) {
|
||||
sk_vm_switch_signal_register(hera_entry.vm_id);
|
||||
/* Seed the switch mechanism's own "who is running" tracker
|
||||
@@ -1265,21 +1272,6 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
* ever happened. */
|
||||
sk_vm_switch_set_current(mama);
|
||||
}
|
||||
if (capsule_vm_find_by_name_nocase("Artemis", &artemis_entry) == 0 &&
|
||||
artemis_entry.state == VM_STATE_LIVE) {
|
||||
sk_vm_switch_signal_register(artemis_entry.vm_id);
|
||||
}
|
||||
/* Hestia is the third fleet member through Phase 4 (FABRIC-3.5.md
|
||||
* SXXXIV.4) -- FABRIC-3.6.md task 1.5, same registration shape as
|
||||
* Artemis above, added here rather than earlier for the
|
||||
* identical reason the comment above this block already gives. */
|
||||
{
|
||||
VMRegistryEntry hestia_entry;
|
||||
if (capsule_vm_find_by_name_nocase("Hestia", &hestia_entry) == 0 &&
|
||||
hestia_entry.state == VM_STATE_LIVE) {
|
||||
sk_vm_switch_signal_register(hestia_entry.vm_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* FABRIC-3.6.md task 3.2 (B1) self-test: confirm every live fleet
|
||||
|
||||
Reference in New Issue
Block a user