diff --git a/FABRIC-3.5.md b/FABRIC-3.5.md index d7cd30d5..f000aaf8 100644 --- a/FABRIC-3.5.md +++ b/FABRIC-3.5.md @@ -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.