FABRIC-3.5.md §XXXVIII: item 38 settled -- decayed heat does not return, and a reaped message returns nothing

Read the path end to end. Message heat is Stadium cell heat, since
MSG-HEAT@/! route through MSG-STADIUM-CELL@ STADIUM-HEAT@/!, so
MSG-COOL-ONE decays header->heat in the cell itself. stadium_evict
returns whatever remains to the owner's reservoir, and its own comment
says why: otherwise every reap leaks heat and the sum drifts below
Q48_ONE. But MSG-REAP fires exactly when heat reaches zero, so at
eviction time the field is already zero.

So the eviction guard that exists to stop reaps leaking is bypassed
completely for every aged-out message, and the whole Q.SLOT pulled to
send it is destroyed. That is worse than §XXXVII.6 anticipated, which
expected partial loss.

The root cause is one field carrying two incompatible meanings: an
activity metric, which should decay, and a reservation currency, which
must not. Decaying a budget token destroys budget. Whether that is
intentional is arguable -- pay to send, forfeit if uncollected is a
coherent backpressure economy -- but nothing replenishes reservoirs, so
the forfeit is permanent against a finite pool.

States a precision boundary rather than overclaiming: there are two heat
accountings, Stadium and VM-physics, and this leak is in the Stadium one.
Whether it is visible to VM-CONSERVED? was not established, so that is
filed as its own item to settle before §XXXIV.6's tripwire is relied on.

Recommends separating reservation from age as two fields, which costs
nothing in greenfield C, removes the leak, keeps reaping working and
preserves backpressure in an honest form. Notes the consequence for
§XXXVII: separating the fields restores §XXXVI.3's original invariant and
deletes the decayed counter §XXXVII.3 had to invent. Both earlier
sections were right about the destination and wrong about the obstacle --
it was never decay, it was decay applied to the wrong field.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
This commit is contained in:
Claude
2026-09-19 11:47:22 +00:00
parent cbed0c5f02
commit b4bd2413d5
+118
View File
@@ -3985,3 +3985,121 @@ reading one function. **Reported, not fixed.**
- ⬜ **Item 37** unchanged — Stage B proves the audit alongside the allocator, and should use
the **scan** to verify the counters (§XXXVII.4).
- Items 32–35 unchanged.
---
## XXXVIII. Item 38 SETTLED: decayed heat does not return — and a reaped message returns nothing at all
§XXXVII.6 flagged this as unestablished and named the one function to read. Read now, end to
end. **The answer is worse than §XXXVII.6 estimated, and the cause is a design conflation
rather than a bug.**
### XXXVIII.1 — The trace
Four facts, each from source:
1. **Message heat *is* Stadium cell heat.** `MSG-HEAT@/!` are not local fields —
`messaging.4th:222-223`: `: MSG-HEAT@ ( m -- q ) MSG-STADIUM-CELL@ STADIUM-HEAT@ ;`. So
`MSG-COOL-ONE` decays `header->heat` **in the Stadium cell itself.**
2. **Eviction returns whatever remains**, and the code knows exactly why
(`stadium.c:459-466`): *"the departing patron's remaining heat must flow back to its owner's
reservoir before the cell returns to the free list, **or every reap leaks heat and
Σ(resident) + reservoir drifts below `Q48_ONE`**."* → `stadium_quotas[slot].reservoir +=
header->heat;`
3. **Reaping fires exactly when heat reaches zero** (`messaging.4th:302-305`):
`MSG-REAP` → `MSG-SCAN @ MSG-HEAT@ 0 = IF … MSG-FREE-NODE`.
4. Therefore, at the moment a naturally-aged message is evicted, `header->heat` is **0**.
> **A message that ages out returns `reservoir += 0`. The entire `Q.SLOT` pulled to send it is
> destroyed.**
**This is not the partial loss §XXXVII.6 anticipated.** The eviction path's heat-return — which
exists specifically to stop reaps leaking — **is bypassed completely for every aged-out
message**, because decay has already drained the field to zero before eviction reads it. The
guard works; decay walks around it.
### XXXVIII.2 — The root cause: one field carrying two incompatible meanings
`header->heat` is used as **both**:
- **An activity metric** — Loop #3's "quiescent words lose heat over time." Decaying this is
correct and is the physics.
- **A reservation currency** — `Q.SLOT` is described in `messaging.4th:44` as "per-item
**admission heat** for `MSG-SEND`/`CH-ACCEPT`," pulled from a reservoir and expected back.
**Decaying a budget token destroys budget.** A message sitting in a queue quietly becomes
cheaper, and on reap refunds nothing. The two meanings are irreconcilable in one field, and the
leak is the visible consequence.
**Whether it is intentional is genuinely arguable.** "You pay to send; if nobody collects, you
forfeit" is a coherent backpressure economy. But nothing in the tree replenishes reservoirs —
every VM starts at `Q48_ONE` and the system only ever conserves or loses — so **the forfeit is
permanent and the pool is finite.** A long-lived VM that sends messages nobody collects
eventually cannot send at all.
### XXXVIII.3 — A precision boundary, stated rather than glossed
**There are two distinct heat accountings in this system, and this finding lands in one of
them:**
- **Stadium accounting** — patron-cell `heat` plus `stadium_quotas[].reservoir`, audited by
`MSG-K` (`MSG-TOTAL-HEAT + CH-TOTAL-HEAT + STADIUM-RES@ + STADIUM-WORD-HEAT`). **This is what
leaks.**
- **VM-physics accounting** — `vm_physics_fleet_heat_sum()` over `execution_heat_q48`, which is
what `vm_physics_conserved()` checks against `Q48_ONE` and what `FABRIC-3.md` §XVIII verified
live as K.
**Whether the two are coupled was not established here**, so this section does **not** claim the
message leak trips `VM-CONSERVED?`. It may be invisible to it entirely. **What it does claim is
narrower and certain: Stadium reservoir heat is destroyed, permanently, at `Q.SLOT` per reaped
message.** Establishing the coupling is worth doing before §XXXIV.6's tripwire is relied on —
**punch item 39.**
### XXXVIII.4 — RECOMMENDATION: separate the reservation from the age
Greenfield C is the moment this costs nothing to fix, because kernel-Hermes is writing the
structure anyway (§XXXIII). **Give a message two fields instead of one:**
| Field | Semantics | Decays? | Returned at free? |
|---|---|---|---|
| **reservation** | the `Q.SLOT` pulled from the sender | **No** | **In full, always** |
| **age** | drives reaping when it expires | **Yes** | n/a |
**The leak disappears**, reaping keeps working (it reads `age`, not the budget), and the
backpressure economy is preserved in its honest form: **sending costs you the reservation for
as long as the message lives, and you get it back when it dies** — rather than being silently
taxed for messages nobody collected.
**Captain Bob's call**, because it is an economic behaviour change, not a repair. Option (a) —
keep the forfeit, document it as intentional — remains legitimate, but should then be *stated*
as a designed sink rather than left looking like the leak `stadium.c:460` was written to
prevent.
### XXXVIII.5 — This makes §XXXVII's ledger simpler, not harder
A pleasing consequence. §XXXVII had to correct §XXXVI's invariant because decay broke it, and
introduced a `decayed` counter to restore exactness:
```
held == pulled − returned − decayed (§XXXVII, with decay in the budget)
held == pulled − returned (with reservation separated from age)
```
**Separating the fields restores §XXXVI.3's original invariant exactly, and deletes the counter
§XXXVII.3 had to invent.** The epsilon stays zero either way — but under this recommendation it
is zero over a *simpler* ledger, with one fewer thing to get wrong.
**Both §XXXVI.3 and §XXXVII.3 were right about the destination and wrong about the obstacle.**
The obstacle was never decay itself; it was decay being applied to the wrong field.
### XXXVIII.6 — Punch list
- ✅ **Item 38 — SETTLED** (§XXXVIII): **no.** Decayed heat does not return, and an aged-out
message returns nothing at all — the eviction guard is bypassed because decay zeroes the field
first.
- ⬜ **Item 39, NEW** — establish whether Stadium accounting and VM-physics accounting are
coupled, i.e. whether this leak is visible to `VM-CONSERVED?` (§XXXVIII.3). **Do this before
relying on §XXXIV.6's tripwire.**
- ⬜ **Item 40, NEW** — rule on §XXXVIII.4: separate reservation from age, or keep the forfeit
and document it as a designed sink.
- Items 32–35, 37 unchanged.