A dead VM leaves kernel-Hermes: KILL/reap drop it from every channel and release its pending mail (found starting M-B)

capsule_vm_kill() and capsule_vm_force_reap() never touched kernel-Hermes:
a dead VM stayed a member of channel 0 and every channel it joined, its
undelivered mail was never released, and anything published to it
afterwards held heat forever. M-B's autonomous events would leak on every
dead subscriber. Captain Bob: "Fix first, own commit."

- sk_hermes_vm_departed(): unsubscribe from every channel, release + pop
  the pending queue (heat back to each funder, ledger exact), free the
  queue entry. Called from both kill and reap, with a HERMES departure line.

Console evidence identical on amd64/aarch64/riscv64: wa with 2 pending ->
"left 2 channel(s), released 2 pending", gone from .CHANNELS, PUBLISH to
its channel delivers 0; acceptance ledger held=2048 (6144 without the
release). Boot parity/dict_hash unchanged; acceptance passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-24 20:09:35 -04:00
co-authored by Claude Opus 5.5
parent 879d00dcf9
commit b562e16ccf
10 changed files with 27662 additions and 0 deletions
+44
View File
@@ -563,3 +563,47 @@ Evidence is named **before** each stage runs.
transcripts. It is pre-existing: the same message is in riscv64 logs back to 2026-09-10,
including this morning's S-1b follow-up acceptance (`-142946`). A TCG timing issue in the
virtio-blk driver, not M-A.
- [x] **Fix, ahead of M-B: a dead VM leaves kernel-Hermes** (found while starting M-B, 2026-09-24).
Captain Bob: *"Fix first, own commit."*
**The defect.** `capsule_vm_kill()` and `capsule_vm_force_reap()` tore a VM down but never
touched kernel-Hermes:
- The dead VM stayed a member of every channel it had joined, channel 0 included. It showed in
`.CHANNELS` as a nameless `?…` member.
- Its undelivered mail was never released.
- Anything published to it afterwards was queued to a VM that would never drain it, its heat
held forever.
M-B makes channel delivery automatic, so every event reaching a dead subscriber would have
leaked.
**The change:**
- `sk_hermes_vm_departed(vm_id, …)` (`kernel_hermes.c`) drops the VM from every channel and
releases and pops its pending queue. Each message's heat goes back to whoever funded it, and
the ledger stays exact. The queue-table entry is freed for reuse.
- `capsule_birth.c` calls it from both `capsule_vm_kill()` and `capsule_vm_force_reap()`, and
reports `HERMES: <name> departed -- left N channel(s), released M pending`.
- `capsule_vm_kill_all_nonmama()` is left alone: its only callers (BYE, cold restart) reset the
machine immediately afterwards.
**Evidence** (console; the same transcript on all three ISAs; `logs/20260924-192807/amd64/`,
`-194215/aarch64/`, `-195620/riscv64/`):
- `CD S" wa" JOIN-VM .` then `.CHANNELS` shows `wa` in channel 0 and `CD`.
- With 2 `SEND`s pending to `wa` (`MAIL?` gives 2, and Hera's `STADIUM-RES@` fell by exactly
4096 = 2 × Q_SLOT), `S" wa" KILL` gives `HERMES: wa departed -- left 2 channel(s), released
2 pending`.
- `.CHANNELS` then shows `ch 0 (4 members): Hera Hestia Artemis wb` and `CD` with 0 members.
- `S" 1 DROP" CD PUBLISH .` gives `0`: nothing queued to the dead VM.
- `wb`, killed with no mail, gives `left 1 channel(s), released 0 pending`.
- **The exact check is the ledger.** On the same boots, acceptance's relay shows `held=2048`,
the one in-flight console message. Without the release, `wa`'s two stranded messages would
have left it at 6144.
- Hera's reservoir is not a clean meter for this: it sits at the word-dispatch floor (Q48_ONE /
3 = 21845), and Stadium word-cooling pushes heat back into it between lines.
**Not exercised:** the `capsule_vm_force_reap()` call. No reap happened in these runs. It
shares the same helper.
**Acceptance** (same boots): boot parity and the `dict_hash` set are unchanged from M-A, and
the O-A checks pass (40/40/48, `HHH`, `3` mid-stream). The relay answers `3`, idle log growth
is 0, there are no faults, and QEMU exits cleanly.