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
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`
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
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.
3. **What transfers, from whom, on every dispatch — and its cost.** **Still open.** This is
real work, not dissolved by the reframing above: on a kernel-build word dispatch, some
Q48.16 amount has to move toward the touched word's cell from the rest of the VM's
resident pool — the same shape as `vm_physics_transfer()`'s clamped subtract/add at the
fleet level, or the proportional redistribution at `capsule_vm_physics.c:334`, but nobody
has picked the exact rule or measured its cost on this path yet. This sits in both the
hosted and kernel dispatch primitive today (`physics_execution_heat_increment()`), but
under the resolution below the *new* transfer is kernel-only, alongside — not replacing —
that existing increment. Still blocks item 4.1's code.
3. ~~**What transfers, from whom, on every dispatch — and its cost.**~~ **RESOLVED
2026-08-05 — a reservoir, not a fan-out, keeping the transfer O(1).**
`vm_physics_touch()`'s proportional pull across every other live VM (`capsule_vm_physics.c
:281-355`) is O(n) over the fleet and is explicitly justified there only because fleet
touches are rare — the file's own comment (`:47-49`) contrasts "dozens to low hundreds" of
fleet touches against word executions "in the millions." Copying that shape for words is
not viable.
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.**~~
**RESOLVED by #2 above** — nothing predates a change that isn't being made to the hashed
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
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
re-derived here:** per-VM Stadium, summing to 1.0 across every patron resident in that VM's
quota (words, blocks, ACLs, messages together, not a word-only sub-pool) — matching §21.4's
"K conserved here, independently" and the `stadium.h:64` field comment. No reset on
admit/evict: the sum stays invariant across *any* call per §19.1's own conservation check, so
admission is itself a transfer, not a reset — mirroring `capsule_vm_physics.c`'s VM-birth
pattern, where a new patron starts at 0 (or a starter share) and is topped up by transfer,
never by manufactured heat.
**What sums to what, and admission semantics — settled by code already written, plus the
reservoir above:** per-VM Stadium, one pool per VM (words, blocks, ACLs, messages together,
not a word-only sub-pool) — matching §21.4's "K conserved here, independently" and the
`stadium.h:64` field comment. **Correction to this section's 2026-08-05 earlier wording:**
the invariant is not "residents sum to `Q48_ONE`" — the reservoir holds whatever residents
haven't claimed, so the correct invariant is
> Σ(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
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
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?
from a designated "unclaimed" pool representing non-resident dictionary words? something
else) and its measured cost on the dispatch path, kernel build only.
- The quantum size (Kconfig constant, §17.7 bullet 3 above) has no value yet — tuning is DoE
work (item 5.1), not invented here, same treatment as `STADIUM_MEMORY_PERCENT` (item 3.2).
- 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'
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
`stadium_admit`/`stadium_dispatch` for words, before the open bullet above reads DECIDED.
`execution_heat`'s current increment/decay behaviour and `dict_hash` are explicitly out of
scope for item 4.1 — this section's earlier ban on touching them no longer applies, because
nothing here proposes touching them.**
**This section now authorizes item 4.1 to wire word patrons onto the Stadium using: the
reservoir-based O(1) touch/cool transfer, Option B's starter-grant admission (no
`execution_heat` involvement), and the corrected invariant above. `execution_heat`'s current
increment/decay behaviour and `dict_hash` remain explicitly out of scope — nothing in item
4.1 touches either.**
---