Add VM-HEAT primitive: groundwork for a compudynamic turn-attractor (FABRIC-3.md §XIX)
Prompted by reading the std79-doe K report: checked whether the campaign's
"real cross-VM dispatch load" was actually concurrent or strictly
serialized. It's serialized at two levels -- VM-EXEC's vm_interpret(target,
...) is a direct synchronous C call (Hera fully blocked until it returns),
and even the background physics tick (vm_tick(), vm_runtime.c) is driven
by each VM's own execution loop, so idle identities accrue zero ticks
between their own turns. K's perfect conservation (FABRIC-3.md §XVIII)
verifies sequential per-VM accounting correctness, not concurrent-access
safety, since there was never concurrent access to test.
Agreed direction: fix this without a scheduler, by reusing the same
least-dense-candidate judgment stadium_admit() already trusts for
eviction, applied to "whose turn is next" instead of "who gets evicted" --
a fleet-level turn-attractor giving the next turn to whichever live
identity currently has the lowest execution_heat_q48, no fixed round-robin,
no priorities, no preemption. Lives beside Stadium in
capsule_vm_physics.c (already the fleet-level consumer of Stadium
primitives, e.g. the K mechanism itself), not inside stadium.c ("the
floor" -- residency/eviction, a different concern from turn order) and
not a new subsystem.
This pass lands only the primitive the mechanism needs: VM-HEAT
( c-addr u -- heat-q48 ), pushing a named VM's current
execution_heat_q48 via vm_physics_heat_of() -- previously C-internal
only (doe_log_heat_by_name(), doe_log.c), never exposed to FORTH. Silent
0 on an unknown/dead name (no print/error), since a turn-attractor
scanning many candidates every turn shouldn't have to filter console
noise for names that simply aren't live. Registered everywhere
VM-EXEC/VM-CALL already are. Builds clean on all three architectures;
live-tested on amd64: Hera -> 65452, Hermes -> 43, unknown name -> 0, no
faults.
The turn-attractor loop itself (std79-doe.fth's trial ordering) is not
yet built -- next step, not done here.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
238ca95b3c
commit
5a9425b91b
+62
@@ -2692,3 +2692,65 @@ wire (same interleaving effect as §XVII's write-up, just landing on a different
|
||||
unaffected either way, since the K result does not depend on trial attribution being correct at
|
||||
all.
|
||||
|
||||
## XIX. VM-HEAT primitive: groundwork for a compudynamic turn-attractor (2026-09-12)
|
||||
|
||||
**The real null hypothesis behind §XVIII's K result.** Reading the report prompted the right
|
||||
question: was execution actually concurrent across the 9 live identity VMs, or strictly
|
||||
serialized? Checked against the code, not assumed: `VM-EXEC` (`mama_word_vm_exec`,
|
||||
`mama_forth_words.c`) calls `vm_interpret(target, cmd_buf)` directly on Hera's own call stack --
|
||||
a synchronous C call, not a thread or coroutine switch. Hera is fully blocked until the target
|
||||
VM's interpretation returns; exactly one VM's FORTH code executes at any instant. Worse, even
|
||||
the *background* physics tick is not uniform across the fleet: `vm_tick()` (`vm_runtime.c`) is
|
||||
called from *each VM's own* execution loop, driven by *that VM's own* word dispatch -- its own
|
||||
doc comment already documents this as a previously-fixed bug (fleet physics used to gate on
|
||||
Hera's tick count specifically and "almost never fired, since Hera-as-orchestrator mostly blocks
|
||||
on VM-EXEC/VM-CALL dispatch that accrues to the target VM's tick count, not hers"). Net effect:
|
||||
while VM A holds the turn, the other 8 identities accrue *zero* ticks and call `vm_tick()` *zero*
|
||||
times -- completely dormant between their own turns, not just "not currently executing a word."
|
||||
|
||||
**What this means for §XVIII's result.** K's perfect conservation verifies that each individual
|
||||
admit/evict/reservoir-transfer event was internally correct across many sequential events on 9
|
||||
different VMs. It is **not** evidence about concurrent-access safety, because there was never any
|
||||
concurrent access for it to be evidence of -- at any instant at most one VM's heat is changing,
|
||||
everything else is static, so the fleet sum staying constant is a real check on the *sequential*
|
||||
accounting, not a stronger claim about contention. Worth being honest about rather than letting
|
||||
the report's "real cross-VM dispatch load" phrasing be read as implying more than it tested.
|
||||
|
||||
**Design direction agreed with Captain Bob: fix the gap without a scheduler.** A fixed
|
||||
round-robin across identities would just be a different hardcoded policy -- not in the spirit of
|
||||
a runtime whose whole design principle is finding its own attractors rather than being told what
|
||||
to do. The chosen direction instead reuses a judgment this codebase already trusts elsewhere:
|
||||
`stadium_admit()` picks the least-dense (coolest) resident as its eviction candidate when a
|
||||
quota is full. Turn allocation is the same kind of decision -- "whose turn is next" instead of
|
||||
"who gets evicted" -- so the plan is a fleet-level turn-attractor that gives the next turn to
|
||||
whichever live identity VM currently has the lowest `execution_heat_q48`, not a fixed sequence
|
||||
position. A VM idle longer becomes relatively cooler and naturally rises to the front; a VM
|
||||
mid-burst stays hot and cedes turns. No priority levels, no time slices, no preemption -- the
|
||||
same density comparison Stadium already uses, applied to "who goes next."
|
||||
|
||||
**Scoping: not the floor, not a parking lot, not a new subsystem.** `stadium.c` is "the floor"
|
||||
in this project's own vocabulary (patrons "leave the floor" when reaped) -- its job is residency
|
||||
and eviction, not turn order, so this doesn't belong inside it. `capsule_vm_physics.c` already
|
||||
is the fleet-level layer that sits next to Stadium and consumes its primitives without living
|
||||
inside `stadium.c` itself (it's exactly how the K mechanism, §XVIII, is built) -- a turn-attractor
|
||||
reading per-VM heat to pick the coolest candidate is precisely the kind of fleet-wide decision
|
||||
this file already exists to make. No new subsystem needed.
|
||||
|
||||
**Groundwork landed this pass: `VM-HEAT ( c-addr u -- heat-q48 )`** (`mama_forth_words.c`,
|
||||
registered in all three places `VM-EXEC`/`VM-CALL` already are -- Hera's root vocabulary, her
|
||||
MAMA vocabulary, and `register_child_vm_words()`). Pushes the named VM's current
|
||||
`execution_heat_q48` via `vm_physics_heat_of()` -- previously C-internal only
|
||||
(`doe_log_heat_by_name()`, `doe_log.c`), never exposed to FORTH. Silent 0 on an unknown/dead
|
||||
name (no print, no error) -- a turn-attractor scanning many candidate names every turn shouldn't
|
||||
have to filter console noise for names that simply aren't live. Builds clean, zero warnings, on
|
||||
all three architectures; live-tested on amd64 (single boot, zuse only): `S" Hera" VM-HEAT .` -->
|
||||
`65452`, `S" Hermes" VM-HEAT .` --> `43`, `S" NoSuchVM" VM-HEAT .` --> `0`, all as expected, no
|
||||
faults.
|
||||
|
||||
**Not yet built: the turn-attractor loop itself.** This pass added only the primitive
|
||||
(`VM-HEAT`) the mechanism needs, not the mechanism -- `std79-doe.fth`'s trial loop still runs
|
||||
each of the 27 shuffled (identity, replicate) cells to completion in shuffle order, unchanged
|
||||
from §XV. The actual heat-driven "coolest live identity goes next" turn loop, and re-verifying
|
||||
the whole campaign (including whether K still holds under genuinely interleaved multi-VM
|
||||
contention, which is the actual point of building this) is the next step, not done here.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user