Files
LithosAnanake/include/starkernel/capsule_vm_physics.h
T
Robert Allan JamesandClaude Opus 5.5 f152a91099 Stage H: DoE rebuilt on BIRTH/SEND/KILL, defs-only workloads; work heat floor on contact (FABRIC-3.8 §3.9, §3.13.7)
Workload capsules the DoE births are definitions only; each defines WL-RUN
(its old birth-time work plus its entry word twice) and WL-DONE-ARGS. The
driver births N workers, SENDs each 'WL-RUN WL-DONE-ARGS SEND', waits in an
ordinary loop for N MU-DONE messages, KILLs them (parked idle), and emits
the marker. The serialized VM-EXEC round-robin is gone.

Kernel: no switch checkpoint after a payload's last word; work heat is the
assessed per-beat charge, and a VM gaining work is raised to the busy set's
floor (Captain Bob's ruling) -- fixes a live stall where a busy-waiting Hera
never yielded, without serializing batch starts.

Evidence: cfg-4 rows spread evenly over Hera and all 8 worker slots (44k
switches); full cfg-4 trials on amd64/aarch64/riscv64 end fail=0 with 8/8
KILL dead; Stage G interleave byte-identical on all three. Acceptance:
dict_hash unchanged, relay 3, idle growth 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 23:48:16 -04:00

220 lines
8.6 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 from mama_word_kill before the registry entry is torn down.
* 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_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 */