Files
Robert Allan JamesandClaude Sonnet 5 c7c9332321 Stadium: real block-patron admission + MIGRATE dispatch (FABRIC-3.md §B)
stadium_admit()'s mass==1 refusal looked like a hard blocker for 1024-byte
blocks, but stadium_word_dispatch()'s real candidate construction proves
Stadium cells carry pure identity/heat/bookkeeping, never the resident's
actual content -- a block patron follows the same shape (identity=LBN,
payload unused), so this was real, scoped work, not a case for stubbing.

New stadium_blocks.h/.c mirror stadium_words.c's admission/cooling shape,
keyed by (quota_slot, lbn) in a fixed-capacity open-addressing hash table
(tombstone deletion) instead of a dense array, since LBN space isn't
densely bounded like word_id. Wired into block_word_block()/buffer()/
update() (block_words.c), __STARKERNEL__-guarded. stadium_dispatch()'s
MIGRATE case now calls blk_flush(lbn) for real instead of printing
"(stub)". Three new Kconfig constants (STADIUM_BLOCK_HEAT_QUANTUM/
STADIUM_BLOCK_COOL_RATE_Q48/STADIUM_BLOCK_TRACK_CAP_MULT) mirror the
word-patron ones, same three-layer wiring.

VM-COOL/DELIVER/EXPIRE stay explicit punch-list items -- VM-COOL
deferred pending the still-iterating Tripod/Zuse/messaging vision,
DELIVER/EXPIRE are their own future subsystem integrations per
FABRIC.md's own "open, not resolved" notes.

Verified clean compile (zero warnings) and clean boot to REPL with
conservation intact (resident_sum + reservoir == Q48_ONE) on all three
architectures (amd64/aarch64/riscv64); BLOCK/BUFFER touches exercised
live from the REPL with no crash; a 22,000-distinct-block flood loop
against an artificially shrunk Stadium ran clean under heavy admission
load. A live MIGRATE console fire was not directly observed this
session (root-caused to a pre-existing reservoir-floor/density-eviction
interaction unrelated to this change, documented in FABRIC-3.md) --
flagged as an honest follow-up, not silently claimed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CXjAPTEKrgY2Mrk25KoLDn
2026-08-25 23:28:59 -04:00

169 lines
8.2 KiB
Plaintext

menu "Kernel-only options"
if STARFORTH_VARIANT_KERNEL
config STARFORTH_ENABLE_VM
bool "Enable StarForth VM integration, M7 milestone (STARFORTH_ENABLE_VM)"
default y
help
Compiles the full StarForth VM source tree into the kernel image
and enables capsule birth/execution. Disabling this builds a
kernel that only reaches the M0-M6 hardware milestones (console,
PMM, VMM, interrupts, timers, kmalloc) with no FORTH interpreter,
no capsules, no "ok" REPL. Gates a large source-file selection
block in Makefile.starkernel, not just a handful of -D flags.
config PARITY_MODE
bool "Deterministic parity harness mode (PARITY_MODE)"
default n
help
Enables the parity/determinism verification harness used to
compare dict_hash and capsule state across independent runs.
Off by default (normal boot); on for parity-campaign builds.
config SK_PARITY_DEBUG
bool "Verbose parity/vocabulary debug logging (SK_PARITY_DEBUG)"
default n
help
Extra diagnostic logging in vocabulary_words.c and
vm_bootstrap.c's parity paths (src/starkernel/vm/vm_internal.h
guards these with #if defined(__STARKERNEL__) && SK_PARITY_DEBUG).
Previously opt-in-only via VM_FEATURE_FLAG_VARS with no Kconfig
presence at all; promoted here for discoverability, same
treatment as every other previously-unwired constant in this
migration.
config STADIUM_VM_MEMORY_PERCENT
int "Percent of remaining kmalloc heap the outer Stadium budgets for VM population (STADIUM_VM_MEMORY_PERCENT)"
default 50
help
Replaces the old fixed STADIUM_MAX_VM_COUNT bound (Captain Bob,
2026-08-15: a hardcoded population ceiling cannot be right when the
actual population is unknowable in advance -- could be 4, could be
4000). The outer Stadium's VM population bound is now computed at
boot, the same way the cell array already is (STADIUM_MEMORY_PERCENT
below): this percentage of the kmalloc heap's remaining free bytes
(kmalloc_get_stats(), taken AFTER the cell array's own allocation),
divided by VM_MEMORY_SIZE (5 MiB, include/vm.h), floored to 1 so Hera
can always boot. No upper ceiling -- birth is refused once the
computed bound is reached (FABRIC.md item 1.5's refusal behaviour is
unchanged), it just isn't a compile-time guess anymore. Default of
50% is an untuned placeholder, not a derived optimum, same DoE-later
treatment as STADIUM_MEMORY_PERCENT.
config STADIUM_CONTAINS_DEPTH_MAX
int "Patron containment chain depth cap (STADIUM_CONTAINS_DEPTH_MAX)"
default 5
help
Hard bound on how many patrons deep a `contains` chain (FABRIC.md
item 1.1, the ninth cell wire) may nest. A patron with a non-none
`contains` link cannot be reaped -- reap-gating enforcement of
this bound is item 3.5's scope, not yet implemented. Distinct
from the already-implemented VM-Stadium nesting depth (item 1.7,
default 2): that bounds VMs nested inside VMs, this bounds
patrons held inside patrons within one Stadium.
config STADIUM_CAPACITY_TICK
int "Fleet transfer-slope re-estimation cadence, in virtual ticks (STADIUM_CAPACITY_TICK)"
default 4000
help
How often vm_physics_heartbeat_tick() re-fits the fleet's
transfer-slope estimate (vm_physics_tick() -- a passive median
recompute over recent touch samples, not a capacity/transfer
decision itself), expressed in virtual ticks -- never wall-clock.
Wired in 2026-08-15 (FABRIC-2.md F.2/§12 Q5): this counter is fed
by EVERY live VM's own vm_tick(), not one VM's, so it previously
shared HEARTBEAT_INFERENCE_FREQUENCY (1000) and fired roughly
(live VM count) times faster than a single VM's own heat-inference
gate -- backwards from the "order of magnitude apart" minimum
(FABRIC.md §22.4). Default of 4000 is a flat, untuned placeholder
picked to roughly restore that separation at Tripod's known
4-VM topology (Hera + two Hermes + Artemis) -- not computed from
live VM count at runtime, deliberately: a fixed constant, same
as every other frequency knob here, not adaptive logic. Real
tuning is DoE work (item 5.1), same treatment as the other
placeholder constants in this file.
config STADIUM_MEMORY_PERCENT
int "Percent of free physical memory the Stadium claims at boot (STADIUM_MEMORY_PERCENT)"
default 1
help
The Stadium's global cell array is sized at boot from
pmm_get_stats().free_bytes, taken at the point of allocation
(FABRIC.md item 3.2, §17.6 position (b): "sized at boot from the
memory budget", not a hardcoded cell count). This is the fraction
of that free-byte figure the array claims, rounded down to whole
64-byte cells. Default of 1% is conservative -- comfortably clears
the ~4096-cells-per-VM illustrative figure in FABRIC.md §23.3
against a QEMU -m 1024 test config while leaving the kernel heap
and everything else nearly all of physical memory.
config STADIUM_WORD_HEAT_QUANTUM
int "Q48.16 heat quantum moved per word touch/starter-grant (STADIUM_WORD_HEAT_QUANTUM)"
default 2048
help
FABRIC.md §17.7 (item 4.1): the fixed Q48.16 amount transferred between
a VM's Stadium reservoir and a word patron's cell on every touch
(already-resident) or starter-grant admission attempt (non-resident,
Option B). Q48_ONE is 65536; the default of 2048 is Q48_ONE divided by
HOTWORDS_CACHE_SIZE (32) -- the population of the cache mechanism this
item retires under __STARKERNEL__ -- so roughly 32 words could hold a
"fully loaded" starter share at once, matching the old cache's slot
count. An untuned placeholder, not a derived optimum: real tuning is
DoE work (item 5.1), same treatment as STADIUM_MEMORY_PERCENT above.
config STADIUM_WORD_COOL_RATE_Q48
int "Q48.16 fraction of resident heat removed per tick (STADIUM_WORD_COOL_RATE_Q48)"
default 21845
help
FABRIC.md §17.7 (item 4.1): redirects Loop #3's decay shape onto
Stadium word-patron heat instead of discarding it -- cooled heat
returns to the VM's reservoir rather than vanishing, so this must be a
fraction of the patron's OWN current heat removed per elapsed tick
(unit-safe for a conserved share of 1.0), not a flat per-tick amount
the way execution_heat's own decay works. Default reuses
INITIAL_DECAY_SLOPE_Q48's numeric value (21845, ~1/3) reinterpreted as
this fraction -- the existing inference engine converged on that
magnitude for the analogous cooling purpose on execution_heat, so it
is a reasonable starting point, not the same quantity. Untuned
placeholder: real tuning is DoE work (item 5.1).
config STADIUM_BLOCK_HEAT_QUANTUM
int "Q48.16 heat quantum moved per block touch/starter-grant (STADIUM_BLOCK_HEAT_QUANTUM)"
default 2048
help
FABRIC-3.md §B (MIGRATE punch-list item): same role as
STADIUM_WORD_HEAT_QUANTUM above, for block patrons touched via
BLOCK/BUFFER/UPDATE (stadium_block_dispatch(), stadium_blocks.c).
Default matches the word quantum -- no evidence yet that blocks need a
different starter share, and diverging without data would just be a
second untuned guess instead of one. Real tuning is future DoE work,
same as the word quantum.
config STADIUM_BLOCK_COOL_RATE_Q48
int "Q48.16 fraction of resident block heat removed per tick (STADIUM_BLOCK_COOL_RATE_Q48)"
default 21845
help
FABRIC-3.md §B (MIGRATE punch-list item): same role as
STADIUM_WORD_COOL_RATE_Q48 above, for block patrons. Default matches
the word cool rate for the same reason the heat quantum above does --
untuned placeholder, not a derived optimum.
config STADIUM_BLOCK_TRACK_CAP_MULT
int "Multiplier on stadium_cell_count() for the block residency table's capacity"
default 2
help
FABRIC-3.md §B (MIGRATE punch-list item): stadium_blocks.c's
(quota_slot, lbn) -> cell_index hash table is a fixed-capacity
open-addressing table sized at stadium_blocks_init() as
stadium_cell_count() * this value. At most stadium_cell_count() blocks
can ever be genuinely Stadium-resident at once (cells are shared across
every patron kind), so a multiplier above 1 exists only to keep probe
chains short under normal open-addressing load factors, not to track
more blocks than could ever be admitted. 2 keeps the load factor at or
below 50%. Untuned placeholder, same as the two constants above.
endif # STARFORTH_VARIANT_KERNEL
endmenu