FABRIC.md: close §17.7 -- reservoir-based O(1) word heat transfer, DECIDED

Resolves the last open question: vm_physics_touch()'s O(n) proportional
fan-out (justified there only by rare fleet touches) isn't viable at word
dispatch frequency. Instead each VM's inner Stadium gets one reservoir
scalar; touch/cool/evict are all O(1) two-party transfers against it,
mirroring Hera's structural role at the fleet level rather than
peer-to-peer redistribution. Admission is the starter grant itself
(Option B): execution_heat stays fully inert in kernel builds, matching
the one-governor rule already committed, at the cost of running
stadium_admit()'s existing O(N) scan on every cold-word dispatch until
item 3.5's free list lands -- accepted debt, not a new gap. Corrects
abb1d92's invariant wording: residents sum to less than Q48_ONE, with
the reservoir holding the remainder.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-05 10:53:47 -04:00
co-authored by Claude Sonnet 5
parent abb1d92190
commit 3d73b8e12a
+76 -28
View File
@@ -1078,7 +1078,7 @@ that matters. The total stays fixed; only the partition shifts.
Decide how big the Stadium is at runtime. Do not decide what an entry is, or how entries Decide how big the Stadium is at runtime. Do not decide what an entry is, or how entries
are addressed, at runtime. are addressed, at runtime.
### 17.7 Word-level heat conservation — LEANING, one question open, blocks item 4.1 ### 17.7 Word-level heat conservation — DECIDED 2026-08-05, blocks item 4.1 until implemented
Item 4.1 (hot words onto the Stadium, §25.5) needs word patrons to carry a Stadium `heat` Item 4.1 (hot words onto the Stadium, §25.5) needs word patrons to carry a Stadium `heat`
share. The obvious move — `q48_from_u64(execution_heat)`, a pure representation change share. The obvious move — `q48_from_u64(execution_heat)`, a pure representation change
@@ -1108,15 +1108,51 @@ resolves four of the five questions this section left open:
today; the counter keeps incrementing and decaying exactly as today. Stadium `heat` is not today; the counter keeps incrementing and decaying exactly as today. Stadium `heat` is not
part of `dict_hash` and item 4.1 does not need to add it there. No baseline discontinuity, part of `dict_hash` and item 4.1 does not need to add it there. No baseline discontinuity,
no "before vs. after" comparison problem — there is nothing to reconcile. no "before vs. after" comparison problem — there is nothing to reconcile.
3. **What transfers, from whom, on every dispatch — and its cost.** **Still open.** This is 3. ~~**What transfers, from whom, on every dispatch — and its cost.**~~ **RESOLVED
real work, not dissolved by the reframing above: on a kernel-build word dispatch, some 2026-08-05 — a reservoir, not a fan-out, keeping the transfer O(1).**
Q48.16 amount has to move toward the touched word's cell from the rest of the VM's `vm_physics_touch()`'s proportional pull across every other live VM (`capsule_vm_physics.c
resident pool — the same shape as `vm_physics_transfer()`'s clamped subtract/add at the :281-355`) is O(n) over the fleet and is explicitly justified there only because fleet
fleet level, or the proportional redistribution at `capsule_vm_physics.c:334`, but nobody touches are rare — the file's own comment (`:47-49`) contrasts "dozens to low hundreds" of
has picked the exact rule or measured its cost on this path yet. This sits in both the fleet touches against word executions "in the millions." Copying that shape for words is
hosted and kernel dispatch primitive today (`physics_execution_heat_increment()`), but not viable.
under the resolution below the *new* transfer is kernel-only, alongside — not replacing —
that existing increment. Still blocks item 4.1's code. Instead, each VM's inner Stadium gets one additional scalar — the **reservoir** — holding
whatever heat is not currently claimed by a resident patron. All word-heat transfers are
two-party, against the reservoir, mirroring Hera's structural role at the fleet level (a
single fixed point that absorbs and donates) rather than the fleet's peer-to-peer fan-out:
- **Touch** (dispatch of an already-resident word): pull a fixed Q48.16 quantum from the
reservoir into the word's cell, clamped at what the reservoir holds. O(1).
- **Cooling** (Loop #3's decay shape, redirected): return heat from the cell to the
reservoir instead of letting it vanish. O(1) per word, same as today's independent decay.
- **Eviction:** the cell's *remaining* heat must flow back to the reservoir before the cell
returns to the free list (item 3.7) — otherwise conservation breaks on every reap.
- **Quantum size:** a Kconfig constant, not an inferred rate. The fleet needed a
statistical estimator (`VMFleetWindow`, `vm_physics_tick()`) because its touches are rare
and irregular; words already have a simpler precedent — `execution_heat`'s existing
per-dispatch increment is a flat `+1`, not tick-scaled. Mirroring that shape avoids a
second estimator. Actual tuning is DoE work (item 5.1), not decided here.
**Admission is the starter grant, by explicit choice (2026-08-05) — `execution_heat`
plays no role.** A non-resident word has no cell, so its density is 0 and it can never win
item 3.5's "denser than the least-dense resident" comparison on its own. Two shapes were
weighed: (A) gate admission attempts on `execution_heat`'s existing threshold crossing —
free, since the increment already happens, but makes `execution_heat` the promotion
governor in kernel builds, directly against the "one governor per build" rule below; or
(B) every dispatch of a non-resident word requests a fixed starter quantum from the
reservoir and is admitted iff that quantum's density beats the current least-dense
resident — `execution_heat` stays fully inert, matching the rule as already committed.
**Chosen: (B).**
**Accepted cost, not a new gap:** `stadium_admit()` (item 3.5, `DONE`) already documents
its own two full-array scans as O(N), deferred until the free list lands (`FABRIC.md
:2971-2974`, "not worth a workaround for code with no caller yet"). Option B calls that
O(N) path on every dispatch of every non-resident word — the same "check on every touch"
shape the old hot-words cache already used (`hotwords_cache_lookup()` in the word-lookup
path, §17.3), but each check costs O(N) here instead of the old cache's O(1)
threshold-plus-round-robin-write. This is deliberately paid for now, in exchange for
keeping `execution_heat` inert, and is expected to resolve the same way item 3.5's own
scans do — when the free list (item 3.7's per-VM free-list heads) replaces the full-array
scan. Not a new item; it collapses into item 3.5's already-recorded debt.
4. ~~**What happens to `dict_hash` and parity comparisons that predate this change.**~~ 4. ~~**What happens to `dict_hash` and parity comparisons that predate this change.**~~
**RESOLVED by #2 above** — nothing predates a change that isn't being made to the hashed **RESOLVED by #2 above** — nothing predates a change that isn't being made to the hashed
field. field.
@@ -1124,14 +1160,22 @@ resolves four of the five questions this section left open:
The Stadium engine (§18) already owns a conserved heat wire per cell; item 4.1 populates The Stadium engine (§18) already owns a conserved heat wire per cell; item 4.1 populates
that existing wire for the word patron kind. It is not a new loop and needs no name. that existing wire for the word patron kind. It is not a new loop and needs no name.
**What sums to what, and admission semantics — settled by code already written, not **What sums to what, and admission semantics — settled by code already written, plus the
re-derived here:** per-VM Stadium, summing to 1.0 across every patron resident in that VM's reservoir above:** per-VM Stadium, one pool per VM (words, blocks, ACLs, messages together,
quota (words, blocks, ACLs, messages together, not a word-only sub-pool) — matching §21.4's not a word-only sub-pool) — matching §21.4's "K conserved here, independently" and the
"K conserved here, independently" and the `stadium.h:64` field comment. No reset on `stadium.h:64` field comment. **Correction to this section's 2026-08-05 earlier wording:**
admit/evict: the sum stays invariant across *any* call per §19.1's own conservation check, so the invariant is not "residents sum to `Q48_ONE`" — the reservoir holds whatever residents
admission is itself a transfer, not a reset — mirroring `capsule_vm_physics.c`'s VM-birth haven't claimed, so the correct invariant is
pattern, where a new patron starts at 0 (or a starter share) and is topped up by transfer,
never by manufactured heat. > Σ(resident patron heat) + reservoir == `Q48_ONE`
checked the same way `vm_physics_conserved()` checks the fleet sum, epsilon-bounded. No reset
on admit/evict: the total stays invariant across *any* call, so admission and eviction are
transfers against the reservoir, never a reset — mirroring `capsule_vm_physics.c`'s VM-birth
pattern (a new patron starts at 0, topped up by transfer) with the reservoir playing Hera's
role: at VM-Stadium-quota-grant time, before any word patron is resident, the reservoir holds
the VM's entire share, exactly as Hera holds the fleet's entire `Q48_ONE` before any other VM
is born.
**One governor per build — states explicitly what closes the §11/§25.5 "never two live heat **One governor per build — states explicitly what closes the §11/§25.5 "never two live heat
mechanisms" gap:** mechanisms" gap:**
@@ -1146,20 +1190,24 @@ mechanisms" gap:**
This is the same per-build split §17.3 already ruled for the cache itself; word-level heat This is the same per-build split §17.3 already ruled for the cache itself; word-level heat
conservation follows it rather than inventing a second shape. conservation follows it rather than inventing a second shape.
**Open, to settle before item 4.1 writes any transfer code:** **Open, deferred honestly rather than blocking:**
- The exact per-dispatch transfer rule (pull from a proportional share of current residents? - The quantum size (Kconfig constant, §17.7 bullet 3 above) has no value yet — tuning is DoE
from a designated "unclaimed" pool representing non-resident dictionary words? something work (item 5.1), not invented here, same treatment as `STADIUM_MEMORY_PERCENT` (item 3.2).
else) and its measured cost on the dispatch path, kernel build only.
- Whether `rolling_window_seed_hotwords_cache()`'s POST warm-start - Whether `rolling_window_seed_hotwords_cache()`'s POST warm-start
(`rolling_window_of_truth.c:786`) needs a Stadium-side counterpart to seed word patrons' (`rolling_window_of_truth.c:786`) needs a Stadium-side counterpart to seed word patrons'
initial `heat` distribution — noted here as the natural seeding site, not designed. initial `heat` distribution from the reservoir — noted here as the natural seeding site,
not designed. Item 4.1 may ship without it; POST warm-start of the *old* cache is unaffected
either way since it writes `execution_heat`, which stays untouched.
- `stadium_admit()`'s O(N) scans (item 3.5's own recorded debt) are accepted cost for word
admission under Option B, not re-litigated here. They resolve when the free list (item 3.7)
supersedes the full-array scan — tracked at item 3.5, not a new item.
**Nothing in this section authorizes touching the per-dispatch transfer rule, or wiring **This section now authorizes item 4.1 to wire word patrons onto the Stadium using: the
`stadium_admit`/`stadium_dispatch` for words, before the open bullet above reads DECIDED. reservoir-based O(1) touch/cool transfer, Option B's starter-grant admission (no
`execution_heat`'s current increment/decay behaviour and `dict_hash` are explicitly out of `execution_heat` involvement), and the corrected invariant above. `execution_heat`'s current
scope for item 4.1 — this section's earlier ban on touching them no longer applies, because increment/decay behaviour and `dict_hash` remain explicitly out of scope — nothing in item
nothing here proposes touching them.** 4.1 touches either.**
--- ---