FABRIC-3.5.md §XXXVII: item 36 settled -- audit epsilon is zero, after correcting §XXXVI's invariant

Checking the numbers first showed §XXXVI.3's proposed invariant cannot
hold. MSG-COOL-ONE multiplies a message's heat by Q-DECAY and writes it
back, returning the difference to nothing, so heat held diverges from
heat pulled with no bug involved -- that is Loop #3's decay working.
§XXXVI's ruling that the sinking condition is a failed self-audit stands;
its arithmetic is corrected in place.

The numbers settle the epsilon on their own. Q48_ONE is 65536,
VM_PHYSICS_EPSILON_Q48 is 3277, and Q.SLOT is about 929 today or 1365
once channels are retired. So the existing epsilon is three and a half
messages' worth of heat -- loose enough to hide the exact failure the
audit exists to catch. It is not wrong, it was sized for fleet heat,
which is genuinely noisy; it must not be reused here.

Corrected invariant: held equals pulled minus returned minus decayed. All
four terms are exact integers, Q.SLOT is a compile-time constant, and
decay's delta is computable at the moment it is applied. Nothing is a
measurement, so there is no noise to tolerate and epsilon is zero -- any
divergence is a bug and the audit fires on the first unit. The one change
required is the whole trick: today decay is an invisible overwrite, so
kernel-Hermes must record what it destroys. One counter converts an
unauditable quantity into an exact one, which is why the epsilon can be
zero rather than guessed.

Cadence is running counters rather than recomputation, since
MSG-TOTAL-HEAT scans the whole arena and as a fleet-wide kernel structure
that is §XXII's O(N) wall a third time, after the pump and the switch
slot table. The check becomes one comparison of four integers, O(1), on
every mutation. A scan stays useful for verifying the counters
themselves, in Stage B and diagnostics rather than the hot path.

Flags without resolving that if decayed heat is destroyed and eviction
returns only what remains, fleet heat drifts downward over a long run --
the same shape F0 §25.7 warned of for a different, since-fixed cause.
States the limit of the trace honestly: STADIUM-EVICT's return semantics
were not read, so this is reported, not established.

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:40:06 +00:00
parent 3a593055de
commit cbed0c5f02
+115
View File
@@ -3870,3 +3870,118 @@ Worth testing, given how much of this document defends against exactly that (§X
They are the same arithmetic seen from two sides, and proving them together costs nothing
extra.
- Items 32–35 unchanged (§XXXV.7).
---
## XXXVII. Item 36 SETTLED: the audit epsilon is zero — and §XXXVI's invariant needed correcting first
### XXXVII.1 — Correction to §XXXVI.3: the invariant as written is wrong
§XXXVI.3 proposed `sum(heat held in live messages) == total_pulled - total_returned`.
**Traced 2026-09-19, that cannot hold, and the reason is by design:**
```forth
: MSG-COOL-ONE ( m -- )
DUP MSG-HEAT@ Q-DECAY Q.* SWAP MSG-HEAT! ;
```
`MSG-COOL-ONE` multiplies a message's heat by `Q-DECAY` (65208/65536 ≈ 0.99499) and **writes it
back. The difference is returned to nothing — it is destroyed.** So a message allocated at
`Q.SLOT` and cooled a few times holds less than was pulled for it, and the two sides of the
invariant diverge **without any bug at all.** This is Loop #3's decay doing its job.
**§XXXVI.3 is corrected here rather than rewritten**, per this series' own rule. The ruling it
made — that the sinking condition is a failed self-audit of kernel-Hermes's own heat accounting
— **stands.** The arithmetic it proposed does not.
### XXXVII.2 — The numbers, which settle the epsilon question on their own
| Quantity | Value |
|---|---|
| `Q48_ONE` | **65536** (`q48_16.h:67`) |
| `VM_PHYSICS_EPSILON_Q48` | **3277** (`capsule_vm_physics.c:57`) — 5% of `Q48_ONE` |
| `Q.SLOT` today = `(Q.1 − Q.1/3) / (32 + 16 − 1)` | ≈ **929** |
| `Q.SLOT` after retiring channels (§XXXIII) = `/32` | ≈ **1365** |
**The existing epsilon is 3.5 messages' worth of heat** at today's `Q.SLOT`, or 2.4 after
§XXXIII. **Kernel-Hermes could lose track of three entire messages and the audit would not
fire.** That is not "too loose" as §XXXVI.7 put it — it is loose enough to hide the exact
failure the audit exists to catch.
`VM_PHYSICS_EPSILON_Q48` is **not wrong**; it was sized for a different quantity — fleet heat,
which is genuinely noisy. **It must not be reused here.**
### XXXVII.3 — RULING: epsilon is zero, made achievable by ledgering the decay
The corrected invariant:
```
sum(heat held in live messages) == total_pulled − total_returned − total_decayed
```
**All four terms are exact integers.** `Q.SLOT` is a compile-time constant, pulls and returns
are symmetric, and decay's per-application delta is *computable at the moment it is applied* —
`heat_before − heat_after`, already in hand. **Nothing here is a measurement. There is no noise
to tolerate.**
> **Epsilon is zero. Any divergence is a bug, and the audit fires on the first unit.**
**The one change required is small and is the whole trick:** today decay is an invisible
overwrite — `MSG-HEAT!` destroys heat and records nothing. **Kernel-Hermes must record what it
destroys.** One counter, incremented by the delta on every decay application. That converts an
unauditable quantity into an exact one, and it is why the epsilon can be zero rather than
guessed.
**This also answers §XXXVI.7's "wrong either way" worry directly:** a guessed epsilon either
cries wolf or never fires. A zero epsilon over an exact ledger does neither — it is a
correctness check, not a threshold.
### XXXVII.4 — Cadence: O(1) counters, never a scan
`MSG-TOTAL-HEAT` (`messaging.4th:267`) computes its sum by **scanning the whole arena** —
`MSG-MAX 0 DO … LOOP`. Fine for 32 slots per VM. **As a fleet-wide kernel structure it is
§XXII's O(N)-per-beat wall again**, the mistake this reshuffle has now identified three times
(the pump, `SK_SWITCH_MAX_SLOTS`, this).
**So the audit maintains running counters rather than recomputing a sum:** `held`, `pulled`,
`returned`, `decayed`, each updated at the three moments they change — allocate, release,
decay. **The check is then one comparison of four integers, O(1), and can run on every
mutation** without a cadence decision at all.
**A scan remains useful as a *verification of the counters themselves*** — run it in Stage B
and in diagnostics, not on the hot path. That is the same split `vm_physics_conserved()`
already uses: cheap invariant continuously, expensive truth occasionally.
### XXXVII.5 — What this does not change
**Fleet-level `vm_physics_conserved()` keeps its 5% band.** Different quantity, genuinely noisy,
and outside this reshuffle. §XXXIV.6's tripwire still uses it as written — the two epsilons
answer different questions and should not be unified.
### XXXVII.6 — Flagged, not resolved: decay means fleet heat is not strictly conserved
**A finding this section surfaced and deliberately does not settle.** If message heat decays and
is destroyed, and `MSG-FREE-NODE`/`STADIUM-EVICT` return only what remains, then heat pulled
from a VM's reservoir **does not fully return to it.** Fleet heat would drift downward over a
long enough run — which is precisely what `FABRIC-0.md` §25.7 warned of for a *different* cause
(`vm_physics_touch()` share truncation, since fixed per `FABRIC-1.md:468`): "a long enough run
would trip `VM-CONSERVED?`. **Nobody has measured the rate.**"
**Limit of this trace, stated honestly:** `STADIUM-EVICT`'s return semantics were **not** read
end to end for this section, so whether the decayed portion is genuinely lost or accounted
elsewhere is **not established here.** It may be intended — decay *is* the physics — in which
case "conservation" means "conserved modulo decay" and the 5% band is doing exactly that job.
**Punch item 38: establish whether decayed message heat returns to the reservoir.** It bears on
§XV.4's "nothing survives but K" and on §XXXIV.6's tripwire, and it is cheap to settle by
reading one function. **Reported, not fixed.**
### XXXVII.7 — Punch list
- ✅ **Item 36 — SETTLED** (§XXXVII): epsilon **zero**; achieved by ledgering decay as an
explicit counter; `VM_PHYSICS_EPSILON_Q48` explicitly **not** reused. §XXXVI.3's arithmetic
corrected in place.
- ⬜ **Item 38, NEW** — does decayed message heat return to the reservoir? (§XXXVII.6)
- ⬜ **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.