Add sk_hermes_decay(), ledgering decay into consumed -- FABRIC-3.6.md task 2.5

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-09-20 18:48:46 -04:00
co-authored by Claude Sonnet 5
parent 493d410028
commit d11eb2e5db
7 changed files with 27737 additions and 4 deletions
+38
View File
@@ -914,7 +914,45 @@ static void kernel_main_deep(BootInfo *boot_info) {
* hand): held == pulled - returned - consumed, at rest. */
if (held2 != pulled2 - returned2 - consumed2) ok = 0;
/* Task 2.5: second cycle, with decay. Allocate to exhaustion
* again, decay every message once, and check `consumed`
* grew by EXACTLY the sum of (heat_before - heat_after)
* measured independently from the Stadium cells, each
* message's new heat is q48_mul(before, Q_DECAY), and the
* audit invariant still holds mid-hold. Then release and
* confirm the reservoir returns reservoir0 minus exactly what
* was consumed (decayed heat does not return, SXL.4). */
uint64_t decay_expected = 0;
uint64_t held3, pulled3, returned3, consumed3;
uint64_t held4, pulled4, returned4, consumed4;
uint64_t n2 = 0;
while (n2 < SK_HERMES_MSG_MAX && sk_hermes_alloc(alloc_test_id, &msg) == 0) {
msgs[n2] = msg;
n2++;
}
if (n2 != expected_n) ok = 0;
for (i = 0; i < n2; i++) {
uint64_t before = stadium_cells()[msgs[i]->stadium_cell].header.heat;
uint64_t want = (uint64_t)q48_mul((q48_16_t)before, (q48_16_t)SK_HERMES_Q_DECAY);
if (sk_hermes_decay(msgs[i]) != 0) ok = 0;
if (stadium_cells()[msgs[i]->stadium_cell].header.heat != want) ok = 0;
decay_expected += before - want;
}
sk_hermes_ledger(&held3, &pulled3, &returned3, &consumed3);
if (decay_expected == 0) ok = 0; /* vacuity guard: decay must bite */
if (consumed3 - consumed2 != decay_expected) ok = 0;
if (held3 != pulled3 - returned3 - consumed3) ok = 0;
for (i = 0; i < n2; i++) {
if (sk_hermes_release(msgs[i]) != 0) ok = 0;
}
sk_hermes_ledger(&held4, &pulled4, &returned4, &consumed4);
if (held4 != held0) ok = 0;
if (consumed4 != consumed3) ok = 0;
if (held4 != pulled4 - returned4 - consumed4) ok = 0;
if (stadium_reservoir_peek(alloc_test_id) != reservoir0 - decay_expected) ok = 0;
console_println(ok ? "PASS" : "FAIL");
print_uint(" decay_consumed=", decay_expected);
print_uint(" reservoir0=", reservoir0);
print_uint(" Q_SLOT=", SK_HERMES_Q_SLOT);
print_uint(" expected_n=", expected_n);
+21
View File
@@ -47,6 +47,7 @@
#include <string.h>
#include "starkernel/vm/kernel_hermes.h"
#include "starkernel/vm/stadium.h"
#include "starkernel/q48_16.h"
static SkHermesMessage sk_hermes_msgs[SK_HERMES_MSG_MAX];
@@ -181,4 +182,24 @@ int sk_hermes_release(SkHermesMessage *msg) {
return rc;
}
int sk_hermes_decay(SkHermesMessage *msg) {
uint64_t before, after;
StadiumCell *cell;
if (!msg || !msg->in_use || msg->stadium_cell < 0) return -1;
cell = &stadium_cells()[msg->stadium_cell];
before = cell->header.heat;
after = (uint64_t)q48_mul((q48_16_t)before, (q48_16_t)SK_HERMES_Q_DECAY);
cell->header.heat = after;
/* Task 2.5: the one site where consumed increments (and where held
* pays for it). SXXXVII.3: decay's delta is exact and in hand here. */
sk_hermes_consumed += before - after;
sk_hermes_held -= before - after;
return 0;
}
#endif /* __STARKERNEL__ */