Files
LithosAnanake/include/starkernel/capsule_vm_physics.h
T
Robert Allan JamesandClaude Opus 5.5 ff8e505308 FABRIC-3.10.8 K-9: remove a dead VM's physics node at KILL and reap
vm_physics_remove() retires the node (heat to the root) and frees it;
capsule_vm_kill() and capsule_vm_force_reap() call it, so WIREBIND/EJECT
kills and reaps now retire too (before, only the KILL word did). A dead
VM now leaves only its registry node (168 B). 3-ISA acceptance clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 10:03:26 -04:00

234 lines
9.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
StarForth — Steady-State Virtual Machine Runtime
Copyright (c) 2023–2025 Robert A. James
All rights reserved.
This file is part of the StarForth project.
Licensed under the StarForth License, Version 1.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at:
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
express or implied, including but not limited to the warranties of
merchantability, fitness for a particular purpose, and noninfringement.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* capsule_vm_physics.h - Dynamic VM Fleet Physics
*
* Structural parallel to the word-level physics engine (execution heat,
* decay, rolling-window history, regression-inferred slope) applied to
* VMs instead of dictionary words. Kernel-only: no hosted-build
* equivalent, since the hosted build has no multi-VM fleet.
*
* sum(execution_heat_q48 for all LIVE VMs) == Q.1 is a conservation
* invariant, held by construction: every state change is a balanced
* transfer (see design doc VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md).
*
* This is a passive observer of VM activity, never a driver of it —
* there is no scheduler here. Recording happens only at existing
* dispatch points (BIRTH, KILL, VM-EXEC, VM-CALL, VM-STEP).
*/
#ifndef STARKERNEL_CAPSULE_VM_PHYSICS_H
#define STARKERNEL_CAPSULE_VM_PHYSICS_H
#include <stdint.h>
#include "starkernel/vm_uuid.h" /* VMUuid -- FABRIC-0.md item 3.8 */
#ifdef __cplusplus
extern "C" {
#endif
/**
* vm_physics_init - Register a newly-born VM at zero heat
*
* Called from mama_word_birth once a VM reaches VM_STATE_LIVE. Starts
* at execution_heat_q48 = 0, so conservation holds trivially — no
* fan-out from existing VMs is needed.
*
* @param vm_id Registry VM ID assigned at birth
*/
void vm_physics_init(VMUuid vm_id);
/**
* vm_physics_retire - Remove a killed VM, returning its heat to Hera
*
* Called by vm_physics_remove() (FABRIC-3.10.8 K-9) before the registry
* entry is torn down; mama_word_kill used to call it directly.
* The dying VM's entire remaining heat is transferred up its
* parent_vm_id chain (VMRegistryEntry, capsule_run.h) to the fleet's
* single structural root, Hera -- no division, no per-survivor
* weighting. The chain walk tolerates already-dead intermediate
* parents (a parent's own parent_vm_id was set once at its birth and
* never rewritten, so the walk continues through it).
*
* @param vm_id Registry VM ID of the VM being killed
*/
void vm_physics_retire(VMUuid vm_id);
/**
* vm_physics_remove - Retire a dead VM, then drop its node
*
* FABRIC-3.10.8 K-9: called from capsule_vm_kill() and
* capsule_vm_force_reap(), while the registry entry is still readable
* (retirement walks parent_vm_id). Heat goes to the root exactly as
* vm_physics_retire(); the node is then unlinked and freed, so a dead VM
* leaves nothing in the fleet list. Hera is refused.
*
* @param vm_id Registry VM ID of the VM being torn down
*/
void vm_physics_remove(VMUuid vm_id);
/**
* vm_physics_touch - Record real dispatch activity against a VM
*
* Called from the three existing dispatch primitives (VM-EXEC, VM-CALL,
* VM-STEP) at the point they've already confirmed the target VM is
* live. Pulls heat from the rest of the fleet toward the touched VM
* (amount = elapsed_ticks * fleet_transfer_slope_q48 >> 16) and appends
* vm_id to the fleet's rolling touch-history window.
*
* Restated on the virtual tick (FABRIC-0.md item 2.1, 2026-08-04): no
* longer takes a wall-clock timestamp. Reads fleet_heartbeat_tick_count
* internally, which is execution-paced (advanced once per vm_tick()
* call, see vm_physics_heartbeat_tick), so the transfer this produces is
* a deterministic function of the execution stream, not of wall time.
*
* @param vm_id Registry VM ID being dispatched to
*/
void vm_physics_touch(VMUuid vm_id);
/**
* vm_physics_charge - Charge the running VM for one beat of its own work
*
* FABRIC-3.8.md §3.13.2 (Stage C-a, 2026-09-23). The work law, beside
* vm_physics_touch()'s dispatch law: called once per beat a VM produces
* while it is the switch-current VM (vm_tick()), so a VM running its own
* kernel-Hermes mail on its own stack heats up as it works -- before this,
* nothing did (G9). Adds one beat's slope to a per-VM carry, and pulls the
* carry's whole units from the rest of the live fleet in proportion to
* each VM's heat, through the same conservative transfer as touch. The
* fraction below one unit, and any whole units lost to the proportional
* split's own rounding, stay in the carry instead of being discarded.
* Records NO fleet_window sample, so the slope estimator and the existing
* DoE heat columns keep their meaning (§3.13.2 "Frozen slope").
*
* @param vm_id Registry VM ID of the running VM
*/
void vm_physics_charge(VMUuid vm_id);
/**
* vm_physics_work_heat_of - Cumulative heat a VM has pulled in by working
*
* FABRIC-3.8.md §3.13.7, ruling C-4 (β), Captain Bob 2026-09-23: heat
* earned by the VM's own work -- the charge vm_physics_charge() assessed at
* each of its beats (beats x slope, in the charge carry's sub-unit scale),
* whether or not the rest of the fleet held heat to move -- independent of
* any structural endowment (Hera's initial Q48_ONE). This is the quantity
* the switch decision compares ("coolest VM with work" = least work heat).
* Never decreases, except that it may be raised by
* vm_physics_work_heat_floor(). 0 for an unknown or dead VM.
*/
uint64_t vm_physics_work_heat_of(VMUuid vm_id);
/**
* vm_physics_work_heat_floor - "Floor on contact" (Captain Bob, Stage H)
*
* Raise vm_id's work heat to floor_q48 if it is below it; never lowers it.
* Called when a VM that had no work gains some (sk_vm_switch_signal_note_
* work_gained()), with the lowest work heat among the VMs that currently
* have work -- so a newly-busy VM joins the running set level with its
* floor instead of at whatever it last had (0 for a new VM), which would
* otherwise let it run alone until it "caught up."
*/
void vm_physics_work_heat_floor(VMUuid vm_id, uint64_t floor_q48);
/**
* vm_physics_tick - Heartbeat-gated inference pass
*
* Mirrors vm_tick_inference_engine: when the fleet touch-history window
* is warm, replays it to re-fit fleet_transfer_slope_q48 via the same
* closed-form log-linear OLS regression used for word-level decay.
* Skips the update (does not substitute a default) when unwarmed or
* when fit quality is not usable.
*
* @param now_ns Current monotonic time in nanoseconds
*/
void vm_physics_tick(uint64_t now_ns);
/**
* vm_physics_heartbeat_tick - Fleet-wide heartbeat, gates vm_physics_tick()
*
* Call from every VM's own vm_tick(), not just Hera's. Increments a
* single fleet-wide tick counter (distinct from any VM's own per-VM
* HeartbeatState.tick_count) and calls vm_physics_tick() once that
* shared counter has advanced by HEARTBEAT_INFERENCE_FREQUENCY since
* the last fit.
*
* Fixes a real defect (VM-FLEET-ATTRACTOR-DESIGN-20260705.md rev k):
* gating this on Hera's own tick_count meant the inference pass almost
* never fired, because Hera-as-orchestrator mostly blocks on VM-EXEC/
* VM-CALL dispatch -- work that accrues to the *target* VM's own tick
* count, not hers. VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md already
* specified the fix's direction: fleet_transfer_slope_q48 must be
* inferred "from aggregate fleet statistics... not one per VM" -- the
* same principle applies to the readiness signal that gates computing
* it, not just the regression's own input trajectory.
*
* @param now_ns Current monotonic time in nanoseconds (of whichever
* VM is calling; unused by vm_physics_tick() today)
*/
void vm_physics_heartbeat_tick(uint64_t now_ns);
/**
* vm_physics_fleet_heat_sum - Sum of execution_heat_q48 over all LIVE VMs
*
* Diagnostic / verification primitive. Should always read Q.1 (65536)
* if the conservation invariant holds.
*/
uint64_t vm_physics_fleet_heat_sum(void);
/**
* vm_physics_heat_of - Current execution_heat_q48 for one VM
*
* Transparency primitive (VM-FLEET-ATTRACTOR-DESIGN-20260705.md): the
* fleet-wide conservation invariant always reads Q.1 by construction, so
* it carries no information about how heat is actually distributed among
* live VMs. This is the per-VM fact that sum alone can't provide.
*
* @param vm_id Registry VM ID
* @return execution_heat_q48, or 0 if vm_id is unknown or not live
*/
uint64_t vm_physics_heat_of(VMUuid vm_id);
/**
* vm_physics_conserved - Conservation check
*
* @return 1 if |vm_physics_fleet_heat_sum() - Q.1| < epsilon, else 0
*/
int vm_physics_conserved(void);
/**
* vm_physics_status - Print a diagnostic status report
*
* Replaces K-STATUS/VM-STATUS. Reports fleet heat sum, conservation
* verdict, and the current fleet-wide inferred slope / fit quality /
* warm-up state -- the fleet-level analog of K-STATUS's per-VM report,
* adapted to a mechanism with no fixed VM count to enumerate.
*/
void vm_physics_status(void);
#ifdef __cplusplus
}
#endif
#endif /* STARKERNEL_CAPSULE_VM_PHYSICS_H */