Correct sk_hermes_alloc() to admit a real Stadium patron; add sk_hermes_release() -- FABRIC-3.6.md tasks 2.2 (amended) + 2.3

Real finding, caught before building release on a foundation that
couldn't support it: task 2.2's first cut of sk_hermes_alloc() pulled
reservoir heat but never admitted a real Stadium-floor patron -- just a
local in_use flag. FABRIC-3.5.md SXXXIII.4 item 1 says MSG-FREE-NODE
returns heat "via STADIUM-EVICT", which only means something if
allocation admitted something. SXL.4's own invariant, Sigma(resident
patron heat) + reservoir + consumed == Q48_ONE, cannot balance if held
heat is invisible to every term while held. Flagged to Captain Bob
before proceeding; authorized to correct 2.2 in the same pass as
building 2.3 on top of the fix.

sk_hermes_alloc() now calls stadium_admit() with heat = the pulled
amount, behaviour = STADIUM_BEHAVIOUR_DELIVER (matching messaging.4th's
own SB-DELIVER STADIUM-ADMIT exactly), and identity = the message's own
slot index (matching the FORTH precedent -- caught live in the first
boot of this fix that omitting this made stadium_dispatch()'s existing
DELIVER diagnostic print msg_idx=0 for every message instead of a
distinct value). The returned cell index is stored in the message's
own stadium_cell field. Stadium-floor refusal (independent of reservoir
affordability) rolls back the pull the same way the other refusal
paths already do.

sk_hermes_release() -- the function task 2.3 actually asks for -- calls
stadium_evict() on that cell, which itself returns the departing
patron's remaining heat to its owning VM's reservoir, matching
MSG-FREE-NODE's exact shape. Release does not touch the reservoir
directly.

Self-test (kernel_main.c) extended: keeps every allocated message's
pointer, allocates to exhaustion as before, releases all of them, and
checks the reservoir returns to precisely its starting value.
"Undecayed" is true by construction (no decay/TTL logic exists yet,
task 2.5) -- exact restoration, not approximate.

Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD, dict_hash unmoved from task 2.2. All three print
identical PASS arithmetic: reservoir0=65536, reservoir_after_alloc=0,
reservoir_final=65536. No compiler warnings.

stadium_dispatch()'s DELIVER-case console output (one line per
eviction) is pre-existing instrumentation, not new -- confirmed real
and load-bearing per stadium.c's own comment, verbose but expected.

Authorized by Captain Bob ("yes").

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-20 04:20:14 -04:00
co-authored by Claude Sonnet 5
parent 9d129fdb1c
commit 2c1dc1753a
11 changed files with 27678 additions and 24 deletions
+57 -1
View File
@@ -551,8 +551,64 @@ Hestia; headless policy intact.
defined as "before and after the alloc/free cycle," not "continuously during." This task's
self-test checks reservoir arithmetic directly instead, which is the right instrument for
what task 2.2 alone claims.
- [ ] **2.3** — Release: return remaining heat via the eviction path. *Check:* reservoir
**AMENDED 2026-09-20, while starting task 2.3 — see that entry for the full account.**
This task's own first cut of `sk_hermes_alloc()` never admitted a real Stadium-floor
patron; it only pulled reservoir heat and set a local flag. Corrected in the same commit
as task 2.3, since release cannot evict something that was never admitted. The two-term
`stadium_conserved()` non-evidence point above still holds — if anything it holds more
precisely now that held heat really is off the floor (in the Stadium-admission sense) and
not merely absent from a two-term check that never modeled it in the first place.
- [x] **2.3** — Release: return remaining heat via the eviction path. *Check:* reservoir
restored exactly for an undecayed message.
2026-09-20 · `logs/20260920-041455/amd64/`, `logs/20260920-041619/aarch64/`,
`logs/20260920-041752/riscv64/` — all three reach `[zuse@Hera] ok>`, zero `UNKNOWN WORD`,
`dict_hash` triple unmoved from task 2.2's baseline. All three print
`Kernel-Hermes alloc/release self-test: ... PASS` with **identical arithmetic**:
`reservoir0=65536 ... reservoir_after_alloc=0 ... reservoir_final=65536` — exact
restoration, not approximate.
**Real finding, not a clean continuation: task 2.2's allocator was missing a step.**
§XXXIII.4 item 1 says `MSG-FREE-NODE` returns heat "via `STADIUM-EVICT`" — which only
means something if allocation admitted a real Stadium patron in the first place. Task
2.2's first cut didn't; it only pulled reservoir heat and flagged a local slot in use.
Caught this before writing release on top of a foundation that couldn't support it, per
§XL.4's own invariant: held message heat that is neither a resident patron nor reservoir
nor `consumed` is invisible to the conservation equation entirely, which cannot be right.
Flagged to Captain Bob before proceeding; authorized to correct task 2.2 in the same pass
as building 2.3 on top of the fix, rather than patching around the gap.
**The fix, amending `src/starkernel/vm/kernel_hermes.c`'s `sk_hermes_alloc()`:** after
pulling `SK_HERMES_Q_SLOT`, now calls `stadium_admit()` with a `StadiumPatronHeader` whose
`heat` is the pulled amount, `behaviour = STADIUM_BEHAVIOUR_DELIVER` (matching
`messaging.4th`'s own `SB-DELIVER STADIUM-ADMIT` at its `MSG-ALLOC` site exactly — same
enum values, `SB-DELIVER`=1=`STADIUM_BEHAVIOUR_DELIVER`), and `identity` set to the
message's own slot index (matching the FORTH precedent, and keeping
`stadium_dispatch()`'s existing `DELIVER` diagnostic — "msg_idx=" — meaningful instead of
every message printing 0; caught live in the first boot of this fix, since all 32
admissions printed `msg_idx=0` before the identity fix). The returned cell index is
stored in the message's own `stadium_cell` field. If the Stadium floor itself refuses
admission (a third refusal path, independent of reservoir affordability), the pull is
rolled back the same way the other refusal paths already do.
**`sk_hermes_release()`, the new function this task actually asks for:** calls
`stadium_evict()` on the message's `stadium_cell` — which itself returns the departing
patron's remaining heat to its owning VM's reservoir (`stadium.c`'s own comment: "the
departing patron's remaining heat must flow back to its owner's reservoir"), so release
does not touch the reservoir directly, matching `MSG-FREE-NODE`'s exact shape
(`DUP 5 CELLS + @ STADIUM-EVICT DROP`). Clears the slot regardless of eviction's own
return value, since a message asked to be released should not remain allocated either way.
**Self-test extended, not replaced:** the existing synthetic-VM self-test (task 2.2, `lo=3`)
now keeps every allocated message's pointer in an array, allocates to exhaustion exactly as
before, then releases all of them and checks the reservoir returns to precisely its
starting value. "Undecayed" is true by construction here — no decay/TTL logic exists yet
(task 2.5) — so this is exactly the case the check asks for: exact restoration, not
approximate. `stadium_dispatch()`'s existing `DELIVER`-case console output (one line per
eviction, pre-existing instrumentation, not new) is verbose but expected — confirmed
real and load-bearing (`stadium.c`'s own comment: "has been real and live on every boot
for weeks"), not something this task introduced. No compiler warnings on either edited
file.
- [ ] **2.4** — The four counters: `held`, `pulled`, `returned`, `consumed` (§XL.4). *Check:*
each increments at exactly one site.
- [ ] **2.5** — Decay: apply, and **record the delta into `consumed`** (§XXXVII.3). *Check:*