FABRIC-3.10.8 K-8: free the vocabulary indexes and inference outputs at vm_cleanup

The ~3.4 KB/VM heap residual left after K-4 was each dead VM's CONTEXT
and FORTH first-character bucket arrays (84 blocks, 3,400 bytes per
worker), plus last_inference_outputs. New vocabulary_free_indexes();
both vm_cleanup()s call it and free the outputs. Per-VM residual is now
232 bytes: the registry node (kept by design) and the vm_physics node
(open finding). 3-ISA acceptance clean, hash 0x99b01134c3d3049c.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-26 09:21:16 -04:00
co-authored by Claude Opus 5.5
parent acea36ea55
commit f29658a65d
15 changed files with 46014 additions and 0 deletions
+31
View File
@@ -673,3 +673,34 @@ riscv64's slowest completion halved (15.1 → 7.7 ms), consistent with requests
| riscv64 | `logs/20260926-082042` | same; **0 `timed out` lines in the log** |
**K-6 closes §8 defect 3.**
### 4.8 K-8 — the ~3.4 KB/VM residual: found and freed (2026-09-26)
**Investigation** (temporary probe: a kmalloc heap walk and two Mama words, `K8-SNAP` (record every used block) and `K8-DIFF` (list the used blocks that are new since the snapshot, with their first 32 bytes)). amd64. Each run did two warm-up worker cycles (WORKER-BIRTH, SEND, KILL), then snapshot, one cycle, diff, twice.
- **Before the fix** (`logs/20260926-083533`): each cycle left **84 new blocks, 3,400 bytes**, identical in both cycles. That is exactly K-4's 95,200 bytes per 28-VM round.
- **What they were:** the dead VM's vocabulary search indexes. These are the first-character bucket arrays for CONTEXT and FORTH (`vm->vocab.ctx_fc` / `forth_fc`), built lazily by `fc_rebuild()` and never freed by `vm_cleanup()`.
- Reading `vm_cleanup()` turned up one more: `vm->last_inference_outputs` was never freed either.
**Change.**
- New `vocabulary_free_indexes(vm)` (`vocabulary_words.c`) frees both indexes' buckets and clears the cached heads.
- Both `vm_cleanup()`s (kernel `vm_core.c`, hosted `vm_bootstrap.c`) call it after the dictionary index, then free `last_inference_outputs`.
**Evidence, after the fix** (same probe, `logs/20260926-084642`): each cycle leaves **2 blocks, 232 bytes**, identical in both cycles. Both are keyed by the dead VM's id:
| Block | Size | What | Status |
|---|---|---|---|
| registry node (`vm_node_t`), state DEAD | 168 B | the VM's registry entry | kept by design: nodes are never freed after boot, so entry pointers stay valid |
| `vm_physics_node_t` | 64 B | the VM's fleet-physics node | the MC-0 finding "`vm_physics` nodes are never removed"; **open, awaiting a ruling** |
- The unexplained residual is gone: 3,400 → 232 bytes per VM, all of it identified.
- K-1's "within one allocation" criterion, as written, cannot be met while those two nodes stay after death. The registry node is a design decision; the physics node depends on the ruling above.
**Standard sequence** (clean build, with the probe reverted; only the four fix files differ from K-6).
| ISA | log | result |
|---|---|---|
| amd64 | `logs/20260926-085034` | hash `0x99b01134c3d3049c`, G A=40 B=40, HHH, all ok `3`, ledger unchanged, 0 B idle, 0 / 0 |
| aarch64 | `logs/20260926-090043` | same |
| riscv64 | `logs/20260926-091122` | same; 0 `timed out` lines |
**K-8 closes the unexplained part of K-1's heap criterion.**