starkernel: item 4.2 -- Hermes native on the Stadium (complete)

Migrates Hermes's message/channel lifecycle onto the Stadium's unified
heat/capacity economy: MSG-ALLOC/FREE-NODE and CH-ALLOC/FREE-NODE now
route entirely through stadium_admit()/stadium_evict(), replacing the
old local free-list + independent heat-field mechanism. Eight
kernel-only STADIUM-* FORTH primitives (ADMIT, EVICT, RES@, RES-PULL,
RES-PUSH, HEAT@, HEAT!, WORD-HEAT), VM.stadium_vm_id threaded through
all three vm_core.c dispatch sites (replacing item 4.1's hardcoded
vm_uuid_hera()), and the stadium_owner[idx] fix so evict-credit lands
in the VM that actually admitted a patron, not whoever owned cell 0.

This session's own contribution, on top of that pre-existing
implementation: found and fixed two bugs blocking the item's own K≡1.0
conservation self-check (HERMES-K was reading 0, not 65536):

- Q.SLOT admission-heat fix (capsules/hermes/init.4th): MSG-SEND/
  CH-ACCEPT admitted with Q.1 (the entire fleet-wide "1.0" unit) per
  item, a leftover from before the Stadium migration when each
  message/channel had its own unconstrained heat field. Instantly
  drained the shared, finite reservoir.

- Reservoir floor for word-execution admission (stadium_words.c):
  stadium_word_dispatch() (item 4.1) pulls STADIUM_WORD_HEAT_QUANTUM on
  every word dispatch, not just first admission -- exhausts a VM's
  entire reservoir in ~32 dispatches, starving any application-level
  economy sharing that VM's reservoir before it gets a chance to pull
  anything. word_dispatch_pull() now clamps word-execution's own pulls
  to leave a Q48_ONE/3 floor (same fair-share figure COMMON-CH's own
  floor already uses); application-level pulls are unaffected.

- STADIUM-WORD-HEAT primitive + stadium_words_resident_heat(): the
  floor deliberately leaves word-execution residents holding real
  heat, invisible to HERMES-K's original formula (MSG+CH+reservoir,
  no term for word patrons). Adding this term closes K to exactly
  65536 on all three architectures.

Also rules on two open scope questions in FABRIC.md: MBR-ALLOC/
MBR-FREE-NODE stay off the Stadium (membership records have no heat
field, never did -- the acceptance bullet's inclusion of them was a
completeness gesture predating a check of the actual layout), and
records the effort number (12 implementation files, +759/-120 lines).

Verified: all three architectures boot clean, full self-test passes,
Stadium conservation closes exactly (resident_sum + reservoir =
Q48_ONE) at both the C/Stadium level and the FORTH-level HERMES-K
check.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-07 01:49:23 -04:00
co-authored by Claude Sonnet 5
parent 0a7f144367
commit 5a28458b21
19 changed files with 1034 additions and 162 deletions
+127 -10
View File
@@ -3344,9 +3344,10 @@ document and committing that amendment as its own item.*
> item never touches reservoir on the donor side), `test-vm reservoir=65536` (a fresh
> `Q48_ONE`, as ruled). `dict_hash` identical across all three and unchanged from item 4.1's
> baseline (`0x3d4e1daf289da94f`), confirming this item added no dictionary word.
- [ ] **4.2 — Hermes native on the Stadium.** The proving ground; produces the effort
- [x] **4.2 — Hermes native on the Stadium.** The proving ground; produces the effort
number. *Refs:* §10. **Unblocked 2026-08-05** — item 4.1a closed; `stadium_grant_quota()`
exists and is wired into every baby VM's birth.
exists and is wired into every baby VM's birth. **Complete 2026-08-07** — all `Done when`
bullets satisfied; see the effort number and MBR-scoping ruling below.
> **Two rulings taken before work starts, 2026-08-05:**
> 1. **`stadium_owner[idx]` fix folded into this item's scope**, by explicit Captain Bob
@@ -3492,8 +3493,83 @@ document and committing that amendment as its own item.*
> the active log threshold and never appears in the serial log, a separate dead end closed
> along the way) is the only viable method for this kernel today.
>
> **Blocker found 2026-08-06, ruled and fixed 2026-08-07 — item 4.1 and item 4.2 silently
> share one finite per-VM reservoir, and word-execution admission alone can exhaust it
> before any application-level allocation runs. This failed the K≡1.0 `Done when` bullet
> below and was not a code bug to just patch — it was a design question spanning both
> items, reported for a ruling rather than resolved unilaterally (§25.0 rule 3). Captain
> Bob ruled option 4 below (reserve a floor); implementation and result are at the end of
> this note.**
>
> After the `Q.SLOT` admission-heat fix (below) closed the original `MSG-SEND`/`CH-ACCEPT`
> over-admission bug, `HERMES-K` still read `0` instead of `65536`. Three prints in one
> boot discriminated the cause: `stadium_reservoir_peek(Hermes)` reads `65536` immediately
> after `BIRTH` (the one-time grant, item 4.1a, is fine) but is already `0` — and
> `COMMON-CH`'s own heat is already `0` — immediately after `CD-INIT` finishes, before
> `HERMES-MSG-TEST`/`MSG-DELIVER-ALL`/anything else in the self-test runs. So this is not a
> Stadium cell getting silently reassigned out from under `COMMON-CH` after the fact
> (aliasing); `COMMON-INIT`'s own `CH-ALLOC` call, partway through `CD-INIT`, never got
> funded in the first place.
>
> Root cause: `stadium_word_dispatch()` (item 4.1, `stadium_words.c:111`) pulls
> `STADIUM_WORD_HEAT_QUANTUM` (2048) from the dispatching VM's reservoir on **every single
> word dispatch**, not just the first time a word is admitted — the "already resident"
> branch (line 142) does `h->heat += stadium_reservoir_pull(vm_id, STADIUM_WORD_HEAT_QUANTUM)`
> unconditionally, every call. `CD-INIT`'s own `MSG-INIT-FREE`/`CH-INIT-FREE`/
> `MBR-INIT-FREE` loops alone dispatch several hundred words (32 + 16 + 64 iterations, each
> several words deep) before `COMMON-INIT` ever runs. At 2048 per dispatch, a VM's entire
> 65536 reservoir is exhausted by roughly 32 total word dispatches — trivially reached
> within `CD-INIT`'s first loop, let alone the rest of Hermes's boot. The boot log's own
> `promotions=145` figure (Hermes's dict-check diagnostics) makes this arithmetic visible
> directly: 145 × 2048 = 296,960, about 4.5× her entire conserved share, from
> word-execution tracking alone. This applies to any VM doing non-trivial work, not
> something specific to Hermes or to messages/channels — Hera's own reservoir has read `0`
> in every log this entire session, for the same reason, just never surfaced as a problem
> because nothing previously tried to spend Hera's reservoir on anything else.
>
> Options, no ranking, not decided here:
> 1. **Separate reservoirs per VM** — one for word-execution tracking (item 4.1), one for
> application-level use (item 4.2 and whatever comes after it). Most invasive: splits
> `stadium_quotas[slot].reservoir` or the one-time grant itself, touches item 4.1's
> already-shipped design and its recorded DoE baseline.
> 2. **Exempt certain VMs from word-execution admission entirely** — e.g., only Hera (or
> only VMs with no item-4.2-style application economy) get word-heat tracking. Requires
> a new per-VM-class distinction that doesn't exist today.
> 3. **Re-scope `STADIUM_WORD_HEAT_QUANTUM`** — smaller, or charged per-unique-word instead
> of per-dispatch. Touches a Kconfig default that already feeds item 4.1's recorded DoE
> measurements; re-tuning it here could invalidate that baseline.
> 4. **Reserve a floor within the shared reservoir** that word-execution admission cannot
> dip below, mirroring `COMMON-CH`'s own `Q.1/3` floor pattern but at the reservoir
> level instead of a single resident. New mechanism, not yet designed.
>
> **Ruling, 2026-08-07: option 4.** Implemented as `word_dispatch_pull()`
> (`stadium_words.c`), a static helper wrapping `stadium_reservoir_pull()` for
> `stadium_word_dispatch()`'s two call sites only (both the already-resident re-heat pull
> and the not-yet-resident starter-grant pull) — clamped so a pull never takes the
> reservoir below `Q48_ONE / 3`, the same "VM-COUNT=3 fair share" figure `COMMON-CH`'s own
> floor already uses, not a new invented number. Application-level pulls
> (`stadium_reservoir_pull()` called directly, e.g. via `STADIUM-RES-PULL`) are untouched —
> only word-execution admission respects the ceiling on its own consumption. Verified: the
> eviction-credit demo now shows a real transfer (`resident_sum` 1612, `reservoir` +1612,
> exactly, when `COMMON-CH` is evicted) instead of the prior `0`→`0` no-op, and the
> Stadium's own conservation line closes exactly on every boot, every architecture:
> `resident_sum=43691 reservoir=21845 sum=65536`.
>
> This alone brought `HERMES-K` from `0` to `43002` — real, but not exact, because
> `HERMES-K`'s formula (`MSG-TOTAL-HEAT CH-TOTAL-HEAT + STADIUM-RES@ +`) has no term for
> word-execution residents' heat, which the floor now deliberately leaves nonzero. Second
> ruling, same date: **add that term.** New accessor `stadium_words_resident_heat(vm_id)`
> (`stadium_words.c`) sums heat over only a VM's own word-execution residents (walking its
> `word_slots` map, not `stadium_resident_sum()`'s full ownership scan, which would double-
> count messages/channels already in `MSG-TOTAL-HEAT`/`CH-TOTAL-HEAT`), exposed as an
> eighth `STADIUM-*` primitive, `STADIUM-WORD-HEAT ( -- heat )`, same implicit-self
> discipline as the other seven. `HERMES-K` becomes
> `MSG-TOTAL-HEAT CH-TOTAL-HEAT + STADIUM-RES@ + STADIUM-WORD-HEAT + ;`. Confirmed on all
> three architectures: `HERMES-K` prints exactly `65536`, K≡1.0, closing the item's
> headline invariant.
>
> *Done when:*
> - The seven `STADIUM-*` FORTH primitives exist, are kernel-only (not in the shared/
> - The eight `STADIUM-*` FORTH primitives exist, are kernel-only (not in the shared/
> vendored word set), and are exercised by at least one Hermes word each.
> - `stadium_owner[idx]` is written correctly on both the free-list-pop and
> eviction-fallback paths in `stadium_admit()`, verified by a resident cell's
@@ -3505,23 +3581,64 @@ document and committing that amendment as its own item.*
> `vm_uuid_hera()` — verified by a Hermes-dispatched word's heat landing in Hermes's
> own reservoir, not Hera's, with both VMs' conservation checks closing independently.
> - Hermes's message and channel lifecycle (`MSG-ALLOC`/`MSG-FREE-NODE`, `CH-ALLOC`/
> `CH-FREE-NODE`, `MBR-ALLOC`/`MBR-FREE-NODE`) run entirely through Stadium admission/
> eviction — no parallel free list, no parallel heat field. Per §11, this is atomic:
> `MSG-HEAT@/!`, `MSG-COOL-ONE`, `MSG-COOL-ALL`, `CH-HEAT@/!`, `CH-COOL-ALL`,
> `CH-TOTAL-HEAT`, `MSG-TOTAL-HEAT` either come out in this same change or are rewritten
> to read/write the Stadium cell instead of a local field — never both mechanisms live
> at once.
> `CH-FREE-NODE`) run entirely through Stadium admission/eviction — no parallel free
> list, no parallel heat field. Per §11, this is atomic: `MSG-HEAT@/!`, `MSG-COOL-ONE`,
> `MSG-COOL-ALL`, `CH-HEAT@/!`, `CH-COOL-ALL`, `CH-TOTAL-HEAT`, `MSG-TOTAL-HEAT` either
> come out in this same change or are rewritten to read/write the Stadium cell instead
> of a local field — never both mechanisms live at once. **`MBR-ALLOC`/`MBR-FREE-NODE`
> ruled out of scope, 2026-08-07 — see below.**
>
> **Ruling, 2026-08-07: `MBR-ALLOC`/`MBR-FREE-NODE` stay on their own free list, not
> migrated onto the Stadium.** This bullet originally named them alongside `MSG-*`/`CH-*`.
> Checked the actual record layout (`capsules/hermes/init.4th`): an MBR record has exactly
> two fields, `MBR-NEXT@` (link) and `MBR-VM@` (owning VM id) — a pure channel-membership
> relationship, no heat field, never had one. The bullet's own stated purpose is "no
> parallel free list, no parallel heat field" — for MBR, "no parallel heat field" is
> already true vacuously, since none exists to be parallel to. Forcing MBR records through
> `stadium_admit()`/`stadium_evict()` would mean inventing a heat/mass/behaviour for
> something structurally without either, spending Stadium cells and reservoir budget on
> records the item's actual design goal (a conserved, evictable-under-pressure heat
> economy) has no reason to govern — "does VM X belong to channel Y" is not a quantity
> that cools, competes for capacity, or needs eviction pressure. Their original inclusion
> in this bullet reads as a completeness gesture written before the field layout was
> checked, not a deliberate requirement. `MBR-ALLOC`/`MBR-FREE-NODE`'s own free list
> (`capsules/hermes/init.4th`, unchanged this item) is correct as-is.
> - Blocks 41104113 (Artemis) are untouched, per `HERMES.md`'s block-map lock. Any new or
> changed Hermes block is verified with `mkcapsule --lint` before commit, per
> `experiments/bare_metal/README.md`.
> - The POST suite (regression gate per §10) passes.
> - **The effort number is recorded explicitly** — per §10, "what Hermes costs is the
> multiplier for everything else." Report at minimum: wall-clock/session time spent,
> lines changed (FORTH + the seven-primitive C surface, split out), and file count
> lines changed (FORTH + the eight-primitive C surface, split out), and file count
> touched, so 4.3/4.4 can be estimated from a real data point rather than guessed.
> - All three architectures boot to `ok>`/`zuse)ok>` with logs under `logs/`, and
> Hermes's own conservation check (K≡1.0 across messages + channels + reservoir) closes
> exactly, reported the same way item 4.1 reported `resident_sum`/`reservoir`/`sum`.
>
> **Effort number, reported 2026-08-07:**
> - **Session time.** This conversation's own boot-log timestamps span roughly 10 hours
> elapsed (`logs/20260806-153504` through `logs/20260807-013712`), covering: the amd64
> GOT-indirect-addressing corruption investigation and fix (unrelated to Stadium logic,
> committed separately as `0a7f144`), the item-4.2 acceptance-status survey against this
> punch-list entry, the `Q.SLOT` admission-heat fix, the word-execution reservoir-floor
> fix, and the `STADIUM-WORD-HEAT` addition that closed K≡1.0. This does **not** include
> whatever time the original seven-primitive implementation and capsule migration
> (already in place when this session's survey began) cost in an earlier session — no
> visibility into that, not estimated rather than guessed.
> - **Lines changed, split FORTH vs. C surface** (`git diff --stat`, this session's
> contribution only — the pre-existing implementation's own diff is included since it
> was still uncommitted when measured, but its authorship/timing is the caveat above):
> - FORTH (`capsules/hermes/init.4th`): +116 / 45 (161 changed), 1 file.
> - C, the eight-primitive `STADIUM-*` surface + Stadium core (`mama_forth_words.c`,
> `stadium.c`, `stadium_words.c`, `stadium.h`, `stadium_words.h`, `vm.h`): +511 / 69
> (580 changed), 6 files.
> - C, other wiring (`capsule_birth.c`, `sk_vm_bootstrap.c`, `vm_core.c`,
> `dictionary_management.c`): +13 / 6 (19 changed), 4 files.
> - Self-test scaffolding (`kernel_main.c`, diagnostic-only, not production code):
> +119 / 0, 1 file.
> - **Total: 12 implementation files, +759 / 120 (879 lines changed).**
> - **File count:** 12 implementation files (13 including this write-up in `FABRIC.md`
> itself).
- [ ] **4.3 — Console.** Settles 1.11 as part of the work. *Refs:* §17.5.
> **Note, 2026-08-05: Captain Bob wants a discussion before any work starts on this item.**
+40 -32
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-05T17:10:15Z -->
<!-- Generated by mkcapsule --manifest 2026-08-07T05:36:57Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
@@ -13,7 +13,7 @@
| `common:msg.4th` | 4055 | `0xa99c5bcd3877f80e` |
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x3d4549142d91ec20` |
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xb6ecf5374e8ee77c` |
| `hermes:init.4th` | 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4114, 4115, 4116, 4117, 4118, 4119, 4120, 4121, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153 | `0xbb77f3ac463f507f` |
| `hermes:init.4th` | 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4114, 4115, 4116, 4117, 4118, 4119, 4120, 4121, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153, 4154, 4155, 4156, 4157, 4158, 4159, 4175, 4176 | `0x85c7b311d1e5bf97` |
| `init-0.4th` | 2200, 2201 | `0xd0a9550baf786bb3` |
| `init-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` |
| `init-2.4th` | 4506, 4515, 4525, 4535, 4545 | `0xf113b3d0bcccae47` |
@@ -109,28 +109,28 @@
| 4063 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4064 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4065 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
| 4100 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4101 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4102 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4103 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4104 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4105 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4106 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4107 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4108 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4109 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4100 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4101 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4102 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4103 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4104 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4105 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4106 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4107 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4108 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4109 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4110 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4111 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4112 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4113 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4114 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4115 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4116 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4117 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4118 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4119 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4120 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4121 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4114 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4115 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4116 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4117 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4118 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4119 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4120 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4121 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4122 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4123 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4124 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
@@ -151,18 +151,24 @@
| 4139 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4140 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4141 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4142 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4143 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4144 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4145 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4146 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4147 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4148 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4149 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4150 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4151 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4152 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4153 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
| 4142 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4143 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4144 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4145 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4146 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4147 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4148 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4149 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4150 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4151 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4152 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4153 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4154 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4155 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4156 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4157 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4158 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4159 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4160 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4161 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4162 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
@@ -178,6 +184,8 @@
| 4172 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4173 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4174 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
| 4175 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4176 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
| 4300 | `process.4th` | `0x781afc1dbd0294f7` | ok |
| 4301 | `process.4th` | `0x781afc1dbd0294f7` | ok |
| 4406 | `init-1.4th` | `0x63e251adb0a03613` | ok |
+116 -45
View File
@@ -15,16 +15,29 @@ Block 4100
64 CONSTANT MBR-MAX
65208 CONSTANT Q-DECAY
255 CONSTANT MSG-DELIVERED
Block 4155
( item 4.2 -- StadiumBehaviour tags, match stadium.h's enum )
0 CONSTANT SB-MIGRATE
1 CONSTANT SB-DELIVER
2 CONSTANT SB-EXPIRE
3 CONSTANT SB-COOL
-1 CONSTANT STADIUM-NONE
( item 4.2 -- per-item admission heat for MSG-SEND/CH-ACCEPT. )
( Remaining reservoir after COMMON-CH's Q.1/3 floor, split )
( evenly across MSG-MAX messages + non-COMMON CH-MAX-1 slots. )
Q.1 Q.1 3 / - MSG-MAX CH-MAX 1- + / CONSTANT Q.SLOT
Block 4101
( Hermes v1 arenas and free-list roots )
( Hermes v1 -- arenas. item 4.2: heat/capacity via Stadium; )
( MBR keeps its own free list (not part of heat economy). )
CREATE MSG-ARENA MSG-MAX MSG-CELLS * CELLS ALLOT
CREATE CH-ARENA CH-MAX CH-CELLS * CELLS ALLOT
CREATE MBR-ARENA MBR-MAX MBR-CELLS * CELLS ALLOT
VARIABLE MSG-FREE-HEAD
VARIABLE CH-FREE-HEAD
VARIABLE MSG-ALLOC-SLOT
VARIABLE CH-ALLOC-SLOT
VARIABLE MBR-FREE-HEAD
VARIABLE MSG-SEQ
VARIABLE CH-ACTIVE
VARIABLE COMMON-CH
Block 4142
( Hermes v1 — VM name routing table )
8 CONSTANT VM-MAX
@@ -39,23 +52,19 @@ CREATE VM-NAME-LENS VM-MAX CELLS ALLOT
S" Hermes" 1 VM-NAME-REG
S" Artemis" 2 VM-NAME-REG ;
Block 4102
( Hermes v1 — MSG-INIT-FREE CH-INIT-FREE )
( Hermes v1 -- INIT-FREE. item 4.2: stamps STADIUM-NONE into )
( each slot's stadium-cell field (msg off 5, ch off 3). )
( Raw offsets: accessors aren't defined yet in file order. )
: MSG-INIT-FREE ( -- )
MSG-MAX 1- 0 DO
I MSG-CELLS * CELLS MSG-ARENA +
I 1+ MSG-CELLS * CELLS MSG-ARENA + SWAP !
LOOP
0 MSG-MAX 1- MSG-CELLS * CELLS MSG-ARENA + !
MSG-ARENA MSG-FREE-HEAD ! ;
MSG-MAX 0 DO
STADIUM-NONE I MSG-CELLS * CELLS MSG-ARENA + 5 CELLS + !
LOOP ;
: CH-INIT-FREE ( -- )
CH-MAX 1- 0 DO
I CH-CELLS * CELLS CH-ARENA +
I 1+ CH-CELLS * CELLS CH-ARENA + SWAP !
LOOP
0 CH-MAX 1- CH-CELLS * CELLS CH-ARENA + !
CH-ARENA CH-FREE-HEAD ! ;
CH-MAX 0 DO
STADIUM-NONE I CH-CELLS * CELLS CH-ARENA + 3 CELLS + !
LOOP ;
Block 4103
( Hermes v1 MBR-INIT-FREE MSG alloc/free )
( Hermes v1 -- MBR-INIT-FREE (member free list, untouched) )
: MBR-INIT-FREE ( -- )
MBR-MAX 1- 0 DO
I MBR-CELLS * CELLS MBR-ARENA +
@@ -63,26 +72,76 @@ Block 4103
LOOP
0 MBR-MAX 1- MBR-CELLS * CELLS MBR-ARENA + !
MBR-ARENA MBR-FREE-HEAD ! ;
: MSG-ALLOC ( -- addr|0 )
MSG-FREE-HEAD @ DUP 0= IF EXIT THEN
DUP @ MSG-FREE-HEAD !
DUP MSG-CELLS CELLS 0 FILL ;
Block 4156
( item 4.2 -- MSG-ALLOC: finds a free slot (TYPE=0) first, )
( admits into Stadium only once confirmed free (no leak). )
: MSG-FIND-FREE-SLOT ( -- addr|0 )
MSG-ARENA MSG-MAX 0 DO
DUP @ 0= IF UNLOOP EXIT THEN
MSG-CELLS CELLS +
LOOP DROP 0 ;
Block 4175
( item 4.2 -- MSG-ALLOC: pulls heat from reservoir first, )
( admits (identity=idx, heat=pulled), rolls back on refusal. )
: MSG-ALLOC ( heat -- addr|0 )
MSG-FIND-FREE-SLOT DUP 0= IF SWAP DROP EXIT THEN
MSG-ALLOC-SLOT !
STADIUM-RES-PULL
MSG-ALLOC-SLOT @ MSG-ARENA - MSG-CELLS CELLS /
SWAP DUP >R
SB-DELIVER STADIUM-ADMIT
DUP STADIUM-NONE = IF
DROP R> STADIUM-RES-PUSH 0 EXIT
THEN
R> DROP
MSG-ALLOC-SLOT @ MSG-CELLS CELLS 0 FILL
MSG-ALLOC-SLOT @ 5 CELLS + !
MSG-ALLOC-SLOT @ ;
Block 4157
( item 4.2 -- MSG-FREE-NODE: evict from Stadium, clear field )
: MSG-FREE-NODE ( addr -- )
MSG-FREE-HEAD @ OVER ! MSG-FREE-HEAD ! ;
DUP 5 CELLS + @ STADIUM-EVICT DROP
DUP 5 CELLS + STADIUM-NONE SWAP !
DROP ;
Block 4104
( Hermes v1 — CH MBR alloc/free )
: CH-ALLOC ( -- addr|0 )
CH-FREE-HEAD @ DUP 0= IF EXIT THEN
DUP @ CH-FREE-HEAD !
DUP CH-CELLS CELLS 0 FILL ;
: CH-FREE-NODE ( addr -- )
CH-FREE-HEAD @ OVER ! CH-FREE-HEAD ! ;
( Hermes v1 -- MBR alloc/free (unchanged; not heat economy) )
: MBR-ALLOC ( -- addr|0 )
MBR-FREE-HEAD @ DUP 0= IF EXIT THEN
DUP @ MBR-FREE-HEAD !
DUP MBR-CELLS CELLS 0 FILL ;
: MBR-FREE-NODE ( addr -- )
MBR-FREE-HEAD @ OVER ! MBR-FREE-HEAD ! ;
Block 4158
( item 4.2 -- CH-FIND-FREE-SLOT. No TYPE field, so freeness )
( is stadium-cell = STADIUM-NONE. CH-ALLOC -> block 4176. )
: CH-FIND-FREE-SLOT ( -- addr|0 )
CH-ARENA CH-MAX 0 DO
DUP 3 CELLS + @ STADIUM-NONE = IF UNLOOP EXIT THEN
CH-CELLS CELLS +
LOOP DROP 0 ;
Block 4176
( item 4.2 -- CH-ALLOC: pulls heat from reservoir first, )
( admits (identity=idx, heat=pulled), rolls back on refusal. )
: CH-ALLOC ( heat -- addr|0 )
CH-FIND-FREE-SLOT DUP 0= IF SWAP DROP EXIT THEN
CH-ALLOC-SLOT !
STADIUM-RES-PULL
CH-ALLOC-SLOT @ CH-ARENA - CH-CELLS CELLS /
SWAP DUP >R
SB-COOL STADIUM-ADMIT
DUP STADIUM-NONE = IF
DROP R> STADIUM-RES-PUSH 0 EXIT
THEN
R> DROP
CH-ALLOC-SLOT @ CH-CELLS CELLS 0 FILL
CH-ALLOC-SLOT @ 3 CELLS + !
CH-ALLOC-SLOT @ ;
Block 4159
( item 4.2 -- CH-FREE-NODE: evict from Stadium, clear field )
: CH-FREE-NODE ( addr -- )
DUP 3 CELLS + @ STADIUM-EVICT DROP
DUP 3 CELLS + STADIUM-NONE SWAP !
DROP ;
Block 4105
( Hermes v1 — message field accessors )
: MSG-TYPE@ ( m -- n ) @ ;
@@ -95,8 +154,9 @@ Block 4105
: MSG-PADDR! ( a m -- ) 3 CELLS + ! ;
: MSG-PLEN@ ( m -- u ) 4 CELLS + @ ;
: MSG-PLEN! ( u m -- ) 4 CELLS + ! ;
: MSG-HEAT@ ( m -- q ) 5 CELLS + @ ;
: MSG-HEAT! ( q m -- ) 5 CELLS + ! ;
( item 4.2: offset 5 = Stadium cell idx. MSG-HEAT@/! -> 4154. )
: MSG-STADIUM-CELL@ ( m -- cell ) 5 CELLS + @ ;
: MSG-STADIUM-CELL! ( cell m -- ) 5 CELLS + ! ;
: MSG-SEQ@ ( m -- n ) 6 CELLS + @ ;
: MSG-SEQ! ( n m -- ) 6 CELLS + ! ;
Block 4143
@@ -114,14 +174,22 @@ Block 4106
: CH-OWNER! ( n c -- ) 1 CELLS + ! ;
: CH-STATE@ ( c -- n ) 2 CELLS + @ ;
: CH-STATE! ( n c -- ) 2 CELLS + ! ;
: CH-HEAT@ ( c -- q ) 3 CELLS + @ ;
: CH-HEAT! ( q c -- ) 3 CELLS + ! ;
( item 4.2: offset 3 = Stadium cell index. CH-HEAT@/! -> 4154. )
: CH-STADIUM-CELL@ ( c -- cell ) 3 CELLS + @ ;
: CH-STADIUM-CELL! ( cell c -- ) 3 CELLS + ! ;
: CH-MBRS@ ( c -- a ) 4 CELLS + @ ;
: CH-MBRS! ( a c -- ) 4 CELLS + ! ;
: CH-NEXT@ ( c -- a ) 5 CELLS + @ ;
: CH-NEXT! ( a c -- ) 5 CELLS + ! ;
: MBR-NEXT@ ( m -- a ) @ ;
: MBR-VM@ ( m -- n ) 1 CELLS + @ ;
Block 4154
( item 4.2 -- composed MSG/CH-HEAT@/!, same names/stacks, )
( new bodies routed via Stadium. Callers need no changes. )
: MSG-HEAT@ ( m -- q ) MSG-STADIUM-CELL@ STADIUM-HEAT@ ;
: MSG-HEAT! ( q m -- ) MSG-STADIUM-CELL@ STADIUM-HEAT! ;
: CH-HEAT@ ( c -- q ) CH-STADIUM-CELL@ STADIUM-HEAT@ ;
: CH-HEAT! ( q c -- ) CH-STADIUM-CELL@ STADIUM-HEAT! ;
Block 4107
( Hermes v1 — deliver )
VARIABLE MSG-LAST-MSG
@@ -134,17 +202,19 @@ VARIABLE MSG-LAST-MSG
DUP MSG-PADDR@ OVER MSG-PLEN@
ROT MSG-TO@ IDX>NAME VM-EXEC ;
Block 4144
( Hermes v1 MSG-SEND )
( Hermes v1 -- MSG-SEND. item 4.2: heat -> MSG-ALLOC's )
( admission directly (zero-heat would lose eviction-fallback )
( density comparisons), not set afterward as before. )
: MSG-SEND ( type from to paddr plen ch -- )
MSG-ALLOC DUP 0= IF 2DROP 2DROP 2DROP DROP EXIT THEN
Q.SLOT MSG-ALLOC DUP 0= IF 2DROP 2DROP 2DROP DROP EXIT THEN
>R
MSG-SEQ @ 1+ DUP MSG-SEQ ! R@ MSG-SEQ!
R@ MSG-CH!
R@ MSG-PLEN! R@ MSG-PADDR!
R@ MSG-TO! R@ MSG-FROM! DUP R@ MSG-TYPE! R@ MSG-ORIG-TYPE!
Q.1 R@ MSG-HEAT! R> DROP ;
R> DROP ;
Block 4108
( Hermes v1 — message cooling )
( Hermes v1 — MSG-COOL-ONE/ALL: linear decay per tick )
VARIABLE MSG-SCAN
: MSG-COOL-ONE ( m -- )
DUP MSG-HEAT@ Q-DECAY Q.* SWAP MSG-HEAT! ;
@@ -236,17 +306,17 @@ Block 4115
CH-SCAN !
REPEAT ;
Block 4116
( Hermes v1 COMMON channel + HERMES-TICK )
( COMMON floor = Q.1/3: Hermes's fair share, VM-COUNT=3 )
VARIABLE COMMON-CH
( Hermes v1 -- COMMON + HERMES-TICK. floor=Q.1/3, VM-COUNT=3 )
( COMMON-CH VARIABLE now in block 4101; see CH-REAP-SAFE 4115 )
: COMMON-INIT ( -- )
CH-ALLOC DUP COMMON-CH !
Q.1 3 / CH-ALLOC DUP COMMON-CH !
0 OVER CH-ID! 0 OVER CH-OWNER!
CH-OPEN OVER CH-STATE!
Q.1 3 / OVER CH-HEAT!
0 OVER CH-MBRS!
CH-ACTIVE @ OVER CH-NEXT!
CH-ACTIVE ! ;
( item 4.2: floor-refresh now reservoir-constrained, may no-op )
( under pressure (was unconstrained write before). Watch log. )
: HERMES-TICK ( -- )
MSG-DELIVER-ALL MSG-REDELIVER-NACKED
MSG-COOL-ALL MSG-REAP
@@ -254,8 +324,10 @@ VARIABLE COMMON-CH
Q.1 3 / COMMON-CH @ CH-HEAT! ;
Block 4147
( Hermes v1 — HERMES-K + WELCOME )
( item 4.2: +STADIUM-WORD-HEAT so word patrons count -- 25.7 )
: HERMES-K ( -- q48 )
MSG-TOTAL-HEAT CH-TOTAL-HEAT + ;
MSG-TOTAL-HEAT CH-TOTAL-HEAT + STADIUM-RES@ +
STADIUM-WORD-HEAT + ;
: WELCOME ( -- ) LOG-INFO" Hermes: loaded" ;
WELCOME
Block 4117
@@ -294,11 +366,10 @@ Block 4119
Block 4148
( Hermes v1 — channel ops: accept confirm close )
: CH-ACCEPT ( -- ch|0 )
CH-ALLOC DUP 0= IF EXIT THEN
Q.SLOT CH-ALLOC DUP 0= IF EXIT THEN
1 CH-MINT-ID OVER CH-ID!
1 OVER CH-OWNER!
CH-NEGOTIATING OVER CH-STATE!
Q.1 OVER CH-HEAT!
0 OVER CH-MBRS!
CH-ACTIVE @ OVER CH-NEXT!
DUP CH-ACTIVE ! ;
BIN
View File
Binary file not shown.
+36
View File
@@ -57,3 +57,39 @@ tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
1,10000,18446744073708981616,0,0,117,4,4,4096,0,0,4895412794951728869,10,65536,0,0,65536,0,0
2,20000,10000,0,0,78,10,7,4096,0,0,0,11,65536,0,0,65536,0,0
3,30000,10000,0,0,101,13,7,4096,0,0,0,13,65536,0,0,65536,0,0
4,40000,10000,0,0,63,19,8,4096,0,0,0,14,65536,0,0,65536,0,0
5,50000,10000,0,0,92,25,8,4096,2,0,0,16,65536,0,0,65536,0,0
6,60000,10000,0,0,64,27,8,4096,2,0,0,18,65536,0,0,65536,0,0
7,70000,10000,0,0,228,32,8,4096,210,0,0,24,65536,0,0,65536,0,0
8,80000,10000,0,0,256,34,9,4096,466,0,0,31,65536,0,0,65536,0,0
9,90000,10000,0,0,256,36,10,4096,722,0,0,37,65536,0,0,65536,0,0
10,100000,10000,0,0,256,36,11,4096,978,0,0,43,65536,0,0,65536,0,0
11,110000,10000,0,0,256,36,11,4096,1234,0,0,50,65536,0,0,65536,0,0
12,120000,10000,0,0,256,36,12,4096,1490,0,0,56,65536,0,0,65536,0,0
13,130000,10000,0,0,256,37,12,4096,1746,0,0,63,65536,0,0,65536,0,0
14,140000,10000,0,0,230,39,12,4096,1949,0,0,69,65536,0,0,65536,0,0
15,150000,10000,0,0,256,39,13,4096,2204,0,0,76,65536,0,0,65536,0,0
16,160000,10000,0,0,256,39,14,4096,2459,0,0,82,65536,0,0,65536,0,0
17,170000,10000,0,0,256,39,15,4096,2713,0,0,89,65536,0,0,65536,0,0
18,180000,10000,0,0,256,39,15,4096,2969,0,0,95,65536,0,0,65536,0,0
19,190000,10000,0,0,256,39,16,4096,3224,0,0,102,65536,0,1,65536,0,0
20,200000,10000,0,0,256,41,17,4096,3480,0,0,109,65536,0,1,65536,0,0
21,210000,10000,0,0,256,41,17,4096,3736,0,0,117,65536,0,1,65536,0,0
22,220000,10000,0,0,256,41,18,4096,3992,0,0,123,65536,0,1,65536,0,0
23,230000,10000,0,0,256,41,19,4096,4096,0,0,130,65536,0,1,65536,0,0
24,240000,10000,0,0,256,41,20,4096,4096,0,0,136,65536,0,1,65536,0,0
25,250000,10000,0,0,256,43,20,2048,2048,0,0,143,65536,0,1,65536,0,0
26,260000,10000,0,0,256,45,21,2048,2048,0,0,149,65536,0,1,65536,0,0
27,270000,10000,0,0,256,45,22,1024,1024,0,0,156,65536,0,1,65536,0,0
28,280000,10000,0,0,256,45,23,1024,1024,0,0,162,65536,0,1,65536,0,0
29,290000,10000,0,0,256,45,24,512,512,0,0,169,65536,0,1,65536,0,0
30,300000,10000,0,0,256,45,24,512,512,0,0,175,65536,0,1,65536,0,0
31,310000,10000,0,0,256,45,25,256,256,0,0,182,65536,0,1,65536,0,0
32,320000,10000,0,0,255,45,26,256,256,0,0,188,65536,0,1,65536,0,0
33,330000,10000,0,0,256,45,27,256,256,0,0,195,65536,0,1,65536,0,0
34,340000,10000,0,0,256,45,27,256,256,0,0,201,65536,0,1,65536,0,0
35,350000,10000,0,0,256,45,28,256,256,0,0,208,65536,0,1,65536,0,0
36,360000,10000,0,0,256,45,29,256,256,0,0,214,65536,0,1,65536,0,0
1 tick_number elapsed_ns tick_interval_ns cache_hits_delta bucket_hits_delta word_executions_delta hot_word_count avg_word_heat_q48 window_width actual_window_size predicted_label_hits jitter_bits apic_ticks time_trust_q48 variance_q48 vm_call_depth_max hera_heat_q48 hermes_heat_q48 artemis_heat_q48
57 56 560000 10000 0 0 211 103 32 4096 1620 0 0 0 65536 0 0 65536 0 0
58 57 570000 10000 0 0 193 110 33 4096 1712 0 0 0 65536 0 0 65536 0 0
59 58 580000 10000 0 0 190 5 67 4096 1775 0 0 0 65536 0 0 65536 0 0
60 1 10000 18446744073708981616 0 0 117 4 4 4096 0 0 4895412794951728869 10 65536 0 0 65536 0 0
61 2 20000 10000 0 0 78 10 7 4096 0 0 0 11 65536 0 0 65536 0 0
62 3 30000 10000 0 0 101 13 7 4096 0 0 0 13 65536 0 0 65536 0 0
63 4 40000 10000 0 0 63 19 8 4096 0 0 0 14 65536 0 0 65536 0 0
64 5 50000 10000 0 0 92 25 8 4096 2 0 0 16 65536 0 0 65536 0 0
65 6 60000 10000 0 0 64 27 8 4096 2 0 0 18 65536 0 0 65536 0 0
66 7 70000 10000 0 0 228 32 8 4096 210 0 0 24 65536 0 0 65536 0 0
67 8 80000 10000 0 0 256 34 9 4096 466 0 0 31 65536 0 0 65536 0 0
68 9 90000 10000 0 0 256 36 10 4096 722 0 0 37 65536 0 0 65536 0 0
69 10 100000 10000 0 0 256 36 11 4096 978 0 0 43 65536 0 0 65536 0 0
70 11 110000 10000 0 0 256 36 11 4096 1234 0 0 50 65536 0 0 65536 0 0
71 12 120000 10000 0 0 256 36 12 4096 1490 0 0 56 65536 0 0 65536 0 0
72 13 130000 10000 0 0 256 37 12 4096 1746 0 0 63 65536 0 0 65536 0 0
73 14 140000 10000 0 0 230 39 12 4096 1949 0 0 69 65536 0 0 65536 0 0
74 15 150000 10000 0 0 256 39 13 4096 2204 0 0 76 65536 0 0 65536 0 0
75 16 160000 10000 0 0 256 39 14 4096 2459 0 0 82 65536 0 0 65536 0 0
76 17 170000 10000 0 0 256 39 15 4096 2713 0 0 89 65536 0 0 65536 0 0
77 18 180000 10000 0 0 256 39 15 4096 2969 0 0 95 65536 0 0 65536 0 0
78 19 190000 10000 0 0 256 39 16 4096 3224 0 0 102 65536 0 1 65536 0 0
79 20 200000 10000 0 0 256 41 17 4096 3480 0 0 109 65536 0 1 65536 0 0
80 21 210000 10000 0 0 256 41 17 4096 3736 0 0 117 65536 0 1 65536 0 0
81 22 220000 10000 0 0 256 41 18 4096 3992 0 0 123 65536 0 1 65536 0 0
82 23 230000 10000 0 0 256 41 19 4096 4096 0 0 130 65536 0 1 65536 0 0
83 24 240000 10000 0 0 256 41 20 4096 4096 0 0 136 65536 0 1 65536 0 0
84 25 250000 10000 0 0 256 43 20 2048 2048 0 0 143 65536 0 1 65536 0 0
85 26 260000 10000 0 0 256 45 21 2048 2048 0 0 149 65536 0 1 65536 0 0
86 27 270000 10000 0 0 256 45 22 1024 1024 0 0 156 65536 0 1 65536 0 0
87 28 280000 10000 0 0 256 45 23 1024 1024 0 0 162 65536 0 1 65536 0 0
88 29 290000 10000 0 0 256 45 24 512 512 0 0 169 65536 0 1 65536 0 0
89 30 300000 10000 0 0 256 45 24 512 512 0 0 175 65536 0 1 65536 0 0
90 31 310000 10000 0 0 256 45 25 256 256 0 0 182 65536 0 1 65536 0 0
91 32 320000 10000 0 0 255 45 26 256 256 0 0 188 65536 0 1 65536 0 0
92 33 330000 10000 0 0 256 45 27 256 256 0 0 195 65536 0 1 65536 0 0
93 34 340000 10000 0 0 256 45 27 256 256 0 0 201 65536 0 1 65536 0 0
94 35 350000 10000 0 0 256 45 28 256 256 0 0 208 65536 0 1 65536 0 0
95 36 360000 10000 0 0 256 45 29 256 256 0 0 214 65536 0 1 65536 0 0
+36
View File
@@ -57,3 +57,39 @@ tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
1,10000,18446744073708981616,0,0,117,4,4,4096,0,0,4895412794951728869,8,65536,0,0,65536,0,0
2,20000,10000,0,0,78,10,7,4096,0,0,0,9,65536,0,0,65536,0,0
3,30000,10000,0,0,101,13,7,4096,0,0,0,11,65536,0,0,65536,0,0
4,40000,10000,0,0,63,19,8,4096,0,0,0,12,65536,0,0,65536,0,0
5,50000,10000,0,0,92,25,8,4096,2,0,0,14,65536,0,0,65536,0,0
6,60000,10000,0,0,64,27,8,4096,2,0,0,15,65536,0,0,65536,0,0
7,70000,10000,0,0,228,32,8,4096,210,0,0,21,65536,0,0,65536,0,0
8,80000,10000,0,0,256,34,9,4096,466,0,0,28,65536,0,0,65536,0,0
9,90000,10000,0,0,256,36,10,4096,722,0,0,34,65536,0,0,65536,0,0
10,100000,10000,0,0,256,36,11,4096,978,0,0,40,65536,0,0,65536,0,0
11,110000,10000,0,0,256,36,11,4096,1234,0,0,46,65536,0,0,65536,0,0
12,120000,10000,0,0,256,36,12,4096,1490,0,0,52,65536,0,0,65536,0,0
13,130000,10000,0,0,256,37,12,4096,1746,0,0,59,65536,0,0,65536,0,0
14,140000,10000,0,0,230,39,12,4096,1949,0,0,65,65536,0,0,65536,0,0
15,150000,10000,0,0,256,39,13,4096,2204,0,0,71,65536,0,0,65536,0,0
16,160000,10000,0,0,256,39,14,4096,2459,0,0,77,65536,0,0,65536,0,0
17,170000,10000,0,0,256,39,15,4096,2713,0,0,83,65536,0,0,65536,0,0
18,180000,10000,0,0,256,39,15,4096,2969,0,0,90,65536,0,0,65536,0,0
19,190000,10000,0,0,256,39,16,4096,3224,0,0,96,65536,0,1,65536,0,0
20,200000,10000,0,0,256,41,17,4096,3480,0,0,102,65536,0,1,65536,0,0
21,210000,10000,0,0,256,41,17,4096,3736,0,0,108,65536,0,1,65536,0,0
22,220000,10000,0,0,256,41,18,4096,3992,0,0,116,65536,0,1,65536,0,0
23,230000,10000,0,0,256,41,19,4096,4096,0,0,123,65536,0,1,65536,0,0
24,240000,10000,0,0,256,41,20,4096,4096,0,0,129,65536,0,1,65536,0,0
25,250000,10000,0,0,256,43,20,2048,2048,0,0,135,65536,0,1,65536,0,0
26,260000,10000,0,0,256,45,21,2048,2048,0,0,142,65536,0,1,65536,0,0
27,270000,10000,0,0,256,45,22,1024,1024,0,0,148,65536,0,1,65536,0,0
28,280000,10000,0,0,256,45,23,1024,1024,0,0,154,65536,0,1,65536,0,0
29,290000,10000,0,0,256,45,24,512,512,0,0,161,65536,0,1,65536,0,0
30,300000,10000,0,0,256,45,24,512,512,0,0,167,65536,0,1,65536,0,0
31,310000,10000,0,0,256,45,25,256,256,0,0,173,65536,0,1,65536,0,0
32,320000,10000,0,0,255,45,26,256,256,0,0,179,65536,0,1,65536,0,0
33,330000,10000,0,0,256,45,27,256,256,0,0,185,65536,0,1,65536,0,0
34,340000,10000,0,0,256,45,27,256,256,0,0,192,65536,0,1,65536,0,0
35,350000,10000,0,0,256,45,28,256,256,0,0,198,65536,0,1,65536,0,0
36,360000,10000,0,0,256,45,29,256,256,0,0,204,65536,0,1,65536,0,0
1 tick_number elapsed_ns tick_interval_ns cache_hits_delta bucket_hits_delta word_executions_delta hot_word_count avg_word_heat_q48 window_width actual_window_size predicted_label_hits jitter_bits apic_ticks time_trust_q48 variance_q48 vm_call_depth_max hera_heat_q48 hermes_heat_q48 artemis_heat_q48
57 56 560000 10000 0 0 211 103 32 4096 1620 0 0 0 65536 0 0 65536 0 0
58 57 570000 10000 0 0 193 110 33 4096 1712 0 0 0 65536 0 0 65536 0 0
59 58 580000 10000 0 0 190 5 67 4096 1775 0 0 0 65536 0 0 65536 0 0
60 1 10000 18446744073708981616 0 0 117 4 4 4096 0 0 4895412794951728869 8 65536 0 0 65536 0 0
61 2 20000 10000 0 0 78 10 7 4096 0 0 0 9 65536 0 0 65536 0 0
62 3 30000 10000 0 0 101 13 7 4096 0 0 0 11 65536 0 0 65536 0 0
63 4 40000 10000 0 0 63 19 8 4096 0 0 0 12 65536 0 0 65536 0 0
64 5 50000 10000 0 0 92 25 8 4096 2 0 0 14 65536 0 0 65536 0 0
65 6 60000 10000 0 0 64 27 8 4096 2 0 0 15 65536 0 0 65536 0 0
66 7 70000 10000 0 0 228 32 8 4096 210 0 0 21 65536 0 0 65536 0 0
67 8 80000 10000 0 0 256 34 9 4096 466 0 0 28 65536 0 0 65536 0 0
68 9 90000 10000 0 0 256 36 10 4096 722 0 0 34 65536 0 0 65536 0 0
69 10 100000 10000 0 0 256 36 11 4096 978 0 0 40 65536 0 0 65536 0 0
70 11 110000 10000 0 0 256 36 11 4096 1234 0 0 46 65536 0 0 65536 0 0
71 12 120000 10000 0 0 256 36 12 4096 1490 0 0 52 65536 0 0 65536 0 0
72 13 130000 10000 0 0 256 37 12 4096 1746 0 0 59 65536 0 0 65536 0 0
73 14 140000 10000 0 0 230 39 12 4096 1949 0 0 65 65536 0 0 65536 0 0
74 15 150000 10000 0 0 256 39 13 4096 2204 0 0 71 65536 0 0 65536 0 0
75 16 160000 10000 0 0 256 39 14 4096 2459 0 0 77 65536 0 0 65536 0 0
76 17 170000 10000 0 0 256 39 15 4096 2713 0 0 83 65536 0 0 65536 0 0
77 18 180000 10000 0 0 256 39 15 4096 2969 0 0 90 65536 0 0 65536 0 0
78 19 190000 10000 0 0 256 39 16 4096 3224 0 0 96 65536 0 1 65536 0 0
79 20 200000 10000 0 0 256 41 17 4096 3480 0 0 102 65536 0 1 65536 0 0
80 21 210000 10000 0 0 256 41 17 4096 3736 0 0 108 65536 0 1 65536 0 0
81 22 220000 10000 0 0 256 41 18 4096 3992 0 0 116 65536 0 1 65536 0 0
82 23 230000 10000 0 0 256 41 19 4096 4096 0 0 123 65536 0 1 65536 0 0
83 24 240000 10000 0 0 256 41 20 4096 4096 0 0 129 65536 0 1 65536 0 0
84 25 250000 10000 0 0 256 43 20 2048 2048 0 0 135 65536 0 1 65536 0 0
85 26 260000 10000 0 0 256 45 21 2048 2048 0 0 142 65536 0 1 65536 0 0
86 27 270000 10000 0 0 256 45 22 1024 1024 0 0 148 65536 0 1 65536 0 0
87 28 280000 10000 0 0 256 45 23 1024 1024 0 0 154 65536 0 1 65536 0 0
88 29 290000 10000 0 0 256 45 24 512 512 0 0 161 65536 0 1 65536 0 0
89 30 300000 10000 0 0 256 45 24 512 512 0 0 167 65536 0 1 65536 0 0
90 31 310000 10000 0 0 256 45 25 256 256 0 0 173 65536 0 1 65536 0 0
91 32 320000 10000 0 0 255 45 26 256 256 0 0 179 65536 0 1 65536 0 0
92 33 330000 10000 0 0 256 45 27 256 256 0 0 185 65536 0 1 65536 0 0
93 34 340000 10000 0 0 256 45 27 256 256 0 0 192 65536 0 1 65536 0 0
94 35 350000 10000 0 0 256 45 28 256 256 0 0 198 65536 0 1 65536 0 0
95 36 360000 10000 0 0 256 45 29 256 256 0 0 204 65536 0 1 65536 0 0
+36
View File
@@ -57,3 +57,39 @@ tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
1,10000,18446744073708981616,0,0,117,4,4,4096,0,0,4895412794951728869,8,65536,0,0,65536,0,0
2,20000,10000,0,0,78,10,7,4096,0,0,0,10,65536,0,0,65536,0,0
3,30000,10000,0,0,101,13,7,4096,0,0,0,11,65536,0,0,65536,0,0
4,40000,10000,0,0,63,19,8,4096,0,0,0,12,65536,0,0,65536,0,0
5,50000,10000,0,0,92,25,8,4096,2,0,0,14,65536,0,0,65536,0,0
6,60000,10000,0,0,64,27,8,4096,2,0,0,15,65536,0,0,65536,0,0
7,70000,10000,0,0,228,32,8,4096,210,0,0,23,65536,0,0,65536,0,0
8,80000,10000,0,0,256,34,9,4096,466,0,0,30,65536,0,0,65536,0,0
9,90000,10000,0,0,256,36,10,4096,722,0,0,37,65536,0,0,65536,0,0
10,100000,10000,0,0,256,36,11,4096,978,0,0,45,65536,0,0,65536,0,0
11,110000,10000,0,0,256,36,11,4096,1234,0,0,52,65536,0,0,65536,0,0
12,120000,10000,0,0,256,36,12,4096,1490,0,0,59,65536,0,0,65536,0,0
13,130000,10000,0,0,256,37,12,4096,1746,0,0,67,65536,0,0,65536,0,0
14,140000,10000,0,0,230,39,12,4096,1949,0,0,74,65536,0,0,65536,0,0
15,150000,10000,0,0,256,39,13,4096,2204,0,0,81,65536,0,0,65536,0,0
16,160000,10000,0,0,256,39,14,4096,2459,0,0,89,65536,0,0,65536,0,0
17,170000,10000,0,0,256,39,15,4096,2713,0,0,96,65536,0,0,65536,0,0
18,180000,10000,0,0,256,39,15,4096,2969,0,0,103,65536,0,0,65536,0,0
19,190000,10000,0,0,256,39,16,4096,3224,0,0,115,65536,0,1,65536,0,0
20,200000,10000,0,0,256,41,17,4096,3480,0,0,122,65536,0,1,65536,0,0
21,210000,10000,0,0,256,41,17,4096,3736,0,0,129,65536,0,1,65536,0,0
22,220000,10000,0,0,256,41,18,4096,3992,0,0,137,65536,0,1,65536,0,0
23,230000,10000,0,0,256,41,19,4096,4096,0,0,144,65536,0,1,65536,0,0
24,240000,10000,0,0,256,41,20,4096,4096,0,0,151,65536,0,1,65536,0,0
25,250000,10000,0,0,256,43,20,2048,2048,0,0,159,65536,0,1,65536,0,0
26,260000,10000,0,0,256,45,21,2048,2048,0,0,166,65536,0,1,65536,0,0
27,270000,10000,0,0,256,45,22,1024,1024,0,0,174,65536,0,1,65536,0,0
28,280000,10000,0,0,256,45,23,1024,1024,0,0,181,65536,0,1,65536,0,0
29,290000,10000,0,0,256,45,24,512,512,0,0,188,65536,0,1,65536,0,0
30,300000,10000,0,0,256,45,24,512,512,0,0,196,65536,0,1,65536,0,0
31,310000,10000,0,0,256,45,25,256,256,0,0,204,65536,0,1,65536,0,0
32,320000,10000,0,0,255,45,26,256,256,0,0,211,65536,0,1,65536,0,0
33,330000,10000,0,0,256,45,27,256,256,0,0,222,65536,0,1,65536,0,0
34,340000,10000,0,0,256,45,27,256,256,0,0,229,65536,0,1,65536,0,0
35,350000,10000,0,0,256,45,28,256,256,0,0,236,65536,0,1,65536,0,0
36,360000,10000,0,0,256,45,29,256,256,0,0,244,65536,0,1,65536,0,0
1 tick_number elapsed_ns tick_interval_ns cache_hits_delta bucket_hits_delta word_executions_delta hot_word_count avg_word_heat_q48 window_width actual_window_size predicted_label_hits jitter_bits apic_ticks time_trust_q48 variance_q48 vm_call_depth_max hera_heat_q48 hermes_heat_q48 artemis_heat_q48
57 56 560000 10000 0 0 211 103 32 4096 1620 0 0 0 65536 0 0 65536 0 0
58 57 570000 10000 0 0 193 110 33 4096 1712 0 0 0 65536 0 0 65536 0 0
59 58 580000 10000 0 0 190 5 67 4096 1775 0 0 0 65536 0 0 65536 0 0
60 1 10000 18446744073708981616 0 0 117 4 4 4096 0 0 4895412794951728869 8 65536 0 0 65536 0 0
61 2 20000 10000 0 0 78 10 7 4096 0 0 0 10 65536 0 0 65536 0 0
62 3 30000 10000 0 0 101 13 7 4096 0 0 0 11 65536 0 0 65536 0 0
63 4 40000 10000 0 0 63 19 8 4096 0 0 0 12 65536 0 0 65536 0 0
64 5 50000 10000 0 0 92 25 8 4096 2 0 0 14 65536 0 0 65536 0 0
65 6 60000 10000 0 0 64 27 8 4096 2 0 0 15 65536 0 0 65536 0 0
66 7 70000 10000 0 0 228 32 8 4096 210 0 0 23 65536 0 0 65536 0 0
67 8 80000 10000 0 0 256 34 9 4096 466 0 0 30 65536 0 0 65536 0 0
68 9 90000 10000 0 0 256 36 10 4096 722 0 0 37 65536 0 0 65536 0 0
69 10 100000 10000 0 0 256 36 11 4096 978 0 0 45 65536 0 0 65536 0 0
70 11 110000 10000 0 0 256 36 11 4096 1234 0 0 52 65536 0 0 65536 0 0
71 12 120000 10000 0 0 256 36 12 4096 1490 0 0 59 65536 0 0 65536 0 0
72 13 130000 10000 0 0 256 37 12 4096 1746 0 0 67 65536 0 0 65536 0 0
73 14 140000 10000 0 0 230 39 12 4096 1949 0 0 74 65536 0 0 65536 0 0
74 15 150000 10000 0 0 256 39 13 4096 2204 0 0 81 65536 0 0 65536 0 0
75 16 160000 10000 0 0 256 39 14 4096 2459 0 0 89 65536 0 0 65536 0 0
76 17 170000 10000 0 0 256 39 15 4096 2713 0 0 96 65536 0 0 65536 0 0
77 18 180000 10000 0 0 256 39 15 4096 2969 0 0 103 65536 0 0 65536 0 0
78 19 190000 10000 0 0 256 39 16 4096 3224 0 0 115 65536 0 1 65536 0 0
79 20 200000 10000 0 0 256 41 17 4096 3480 0 0 122 65536 0 1 65536 0 0
80 21 210000 10000 0 0 256 41 17 4096 3736 0 0 129 65536 0 1 65536 0 0
81 22 220000 10000 0 0 256 41 18 4096 3992 0 0 137 65536 0 1 65536 0 0
82 23 230000 10000 0 0 256 41 19 4096 4096 0 0 144 65536 0 1 65536 0 0
83 24 240000 10000 0 0 256 41 20 4096 4096 0 0 151 65536 0 1 65536 0 0
84 25 250000 10000 0 0 256 43 20 2048 2048 0 0 159 65536 0 1 65536 0 0
85 26 260000 10000 0 0 256 45 21 2048 2048 0 0 166 65536 0 1 65536 0 0
86 27 270000 10000 0 0 256 45 22 1024 1024 0 0 174 65536 0 1 65536 0 0
87 28 280000 10000 0 0 256 45 23 1024 1024 0 0 181 65536 0 1 65536 0 0
88 29 290000 10000 0 0 256 45 24 512 512 0 0 188 65536 0 1 65536 0 0
89 30 300000 10000 0 0 256 45 24 512 512 0 0 196 65536 0 1 65536 0 0
90 31 310000 10000 0 0 256 45 25 256 256 0 0 204 65536 0 1 65536 0 0
91 32 320000 10000 0 0 255 45 26 256 256 0 0 211 65536 0 1 65536 0 0
92 33 330000 10000 0 0 256 45 27 256 256 0 0 222 65536 0 1 65536 0 0
93 34 340000 10000 0 0 256 45 27 256 256 0 0 229 65536 0 1 65536 0 0
94 35 350000 10000 0 0 256 45 28 256 256 0 0 236 65536 0 1 65536 0 0
95 36 360000 10000 0 0 256 45 29 256 256 0 0 244 65536 0 1 65536 0 0
+64
View File
@@ -307,6 +307,33 @@ void stadium_reservoir_push(VMUuid vm_id, uint64_t amount);
*/
uint64_t stadium_reservoir_peek(VMUuid vm_id);
/*
* stadium_quota_slot_for_vm - Read-only: vm_id's quota slot index (0 to
* STADIUM_MAX_VM_COUNT-1), for callers outside stadium.c that need to key
* their own per-VM state the same way stadium.c's internal arrays already
* do (FABRIC.md §25.5 item 4.2 -- stadium_words.c's word_id -> cell_index
* map needs this to stop colliding across VMs; word_id is scoped per-VM,
* not globally unique, so a single shared map aliases different VMs' words
* onto each other's Stadium cells and reservoirs).
*
* @param vm_id VM to look up.
* @return Quota slot index, or -1 if vm_id holds no quota.
*/
int stadium_quota_slot_for_vm(VMUuid vm_id);
/*
* stadium_resident_sum - Read-only: sum of heat across every cell currently
* resident AND owned by vm_id's own quota (FABRIC.md §25.5 item 4.2 --
* boot diagnostics need this filtered per-VM once a second VM holds a
* quota; summing every resident cell regardless of owner, as the pre-4.2
* diagnostic did, mixes two VMs' conservation totals together).
* Returns 0 for an unknown vm_id, same convention as stadium_reservoir_peek().
*
* @param vm_id Owning VM's id.
* @return Sum of resident heat owned by vm_id (Q48.16), or 0 if vm_id has no quota.
*/
uint64_t stadium_resident_sum(VMUuid vm_id);
/*
* stadium_evict - Reap the patron header at cell_index (FABRIC.md §17.2:
* "reap means leaves the floor, not destroyed"). Dispatches its behaviour
@@ -430,6 +457,43 @@ size_t stadium_admit(VMUuid vm_id, const StadiumPatronHeader *candidate);
*/
int stadium_grant_quota(VMUuid new_vm_id, VMUuid from_vm_id);
/*
* stadium_cell_heat_get - Read a resident cell's own heat (FABRIC.md item
* 4.2's fourth ruling). Requires cell_index to be resident AND owned by
* vm_id's quota -- returns 0 otherwise (out of range, not resident, or
* belongs to a different VM), same ambiguity-with-a-genuine-zero already
* accepted by stadium_reservoir_peek()'s doc: callers that need to
* distinguish "refused" from "actually zero" must already know the cell is
* theirs (e.g. from their own resident-cell tracking), same contract as
* every other implicit-self primitive here.
*
* @param vm_id Calling VM's own identity.
* @param cell_index Index of the resident patron header to read.
* @return The cell's current heat (Q48.16), or 0 if refused.
*/
uint64_t stadium_cell_heat_get(VMUuid vm_id, size_t cell_index);
/*
* stadium_cell_heat_set - Write a resident cell's own heat, reconciling the
* reservoir delta atomically (FABRIC.md item 4.2's fourth ruling). Same
* ownership requirement as stadium_cell_heat_get(). If new_heat is higher
* than the cell's current heat, pulls the exact difference from vm_id's own
* reservoir first -- refuses (returns -1, no mutation) if the reservoir
* cannot cover the full increase, never a partial credit that would invent
* heat. If new_heat is lower, pushes the exact difference back to the
* reservoir after writing. Equal is a no-op success. This is the only
* sanctioned way to change a resident cell's heat post-admission -- doing
* the reservoir accounting here, not leaving it to the FORTH caller, is the
* whole reason this primitive exists rather than a raw field poke.
*
* @param vm_id Calling VM's own identity.
* @param cell_index Index of the resident patron header to write.
* @param new_heat The heat value to set (Q48.16).
* @return 0 on success, -1 if refused (not owned/resident, or insufficient
* reservoir for an increase).
*/
int stadium_cell_heat_set(VMUuid vm_id, size_t cell_index, uint64_t new_heat);
#endif /* __STARKERNEL__ */
#endif /* STARKERNEL_VM_STADIUM_H */
+63 -25
View File
@@ -48,9 +48,17 @@
/*
* stadium_words_init - Zeroes the word_id -> cell_index map (DICTIONARY_SIZE
* entries, static -- no allocation). Must be called after stadium_boot_init()
* and stadium_birth_hera(), before any word ever dispatches. Safe to call
* again (re-zeroes); nothing does today.
* entries per VM quota slot, STADIUM_MAX_VM_COUNT slots, static -- no
* allocation). Must be called after stadium_boot_init() and
* stadium_birth_hera(), before any word ever dispatches. Safe to call again
* (re-zeroes for every slot); nothing does today.
*
* item 4.2 (FABRIC.md §25.5): the map is keyed by quota slot, not just
* word_id -- word_id is assigned per-VM (vm->next_word_id), not globally
* unique, so a single shared word_id -> cell_index map aliased different
* VMs' words onto each other's Stadium cells and reservoirs the moment a
* second VM (Hermes) held a quota. One system-wide init call still covers
* every slot; no per-VM init call is needed.
*/
void stadium_words_init(void);
@@ -65,25 +73,34 @@ void stadium_words_init(void);
* (fraction of the cell's own current heat, scaled by elapsed_ticks since
* this word's own last touch -- STADIUM_WORD_COOL_RATE_Q48) crediting the
* cooled amount back to vm_id's reservoir, then pulls
* STADIUM_WORD_HEAT_QUANTUM from the reservoir into the cell (both clamped
* to what the reservoir actually holds).
* STADIUM_WORD_HEAT_QUANTUM from the reservoir into the cell -- clamped to
* what the reservoir actually holds AND to a floor of Q48_ONE / 3 that
* word-execution admission alone may never dip the reservoir below
* (FABRIC.md §25.7, Captain Bob's ruling 2026-08-06: this pull fires on
* EVERY dispatch, not just first admission, and without a floor exhausts a
* VM's entire reservoir in ~32 dispatches, starving any application-level
* economy -- e.g. item 4.2's Hermes -- sharing the same VM's reservoir).
* Application-level pulls (stadium_reservoir_pull() called directly) are
* not subject to this floor.
*
* If word_id is not resident (or the map's entry is stale -- self-healing
* check against the cell's discriminator bit and identity, covers both a
* prior eviction and a FORGET/redefine word_id reuse this function did not
* itself clear): attempts Option B starter-grant admission -- pulls
* STADIUM_WORD_HEAT_QUANTUM from the reservoir, builds an unpinned COOL
* candidate, calls stadium_admit(). On refusal, pushes the pulled quantum
* back (rollback, preserves conservation across the failed attempt). On
* success, records the mapping and increments the promotion counter.
* STADIUM_WORD_HEAT_QUANTUM from the reservoir (same floor as above),
* builds an unpinned COOL candidate, calls stadium_admit(). On refusal,
* pushes the pulled quantum back (rollback, preserves conservation across
* the failed attempt). On success, records the mapping and increments the
* promotion counter.
*
* No-op if word_id == WORD_ID_INVALID, word_id >= DICTIONARY_SIZE, or the
* word layer has not been initialized.
* No-op if word_id == WORD_ID_INVALID, word_id >= DICTIONARY_SIZE, or vm_id
* holds no Stadium quota.
*
* @param vm_id Owning VM. Hardcoded to vm_uuid_hera() at every
* call site today -- Tripod is pruned to Hera alone
* (item 0.1), and she is the only VM with a Stadium
* quota. Revisit when item 4.2 restores Hermes.
* @param vm_id Owning VM -- vm->stadium_vm_id at every call site.
* Scopes the word_id -> cell_index lookup to this
* VM's own quota slot (item 4.2, FABRIC.md §25.5) so
* two VMs' independently-numbered word_ids cannot
* alias onto each other's cells/reservoirs.
* @param word_id The dispatching DictEntry's stable word_id.
* @param heartbeat_ticks Current vm->heartbeat.tick_count (virtual tick,
* never wall-clock -- same convention as every other
@@ -99,12 +116,34 @@ void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ti
* its stale heat (same failure class as the 2026-08-02 block_words.c
* aliasing bug). Evicts the cell if word_id is resident (crediting its heat
* back to the reservoir via stadium_evict()'s own credit path) and clears
* the map entry. No-op if word_id is not resident, out of range, or the
* word layer is not initialized.
* the map entry. No-op if word_id is not resident, out of range, or vm_id
* holds no Stadium quota.
*
* @param vm_id Owning VM -- vm->stadium_vm_id (item 4.2, FABRIC.md §25.5:
* scopes the lookup to this VM's own word_slots, same
* reason stadium_word_dispatch() takes it).
* @param word_id The DictEntry's word_id, about to be recycled.
*/
void stadium_word_forget(uint32_t word_id);
void stadium_word_forget(VMUuid vm_id, uint32_t word_id);
/*
* stadium_words_resident_heat - Sum of heat held by vm_id's own
* word-execution residents only (item 4.1's cells, tracked in this file's
* own word_slots map) -- NOT messages/channels/other application residents,
* which stadium_resident_sum() (stadium.h, item 4.2) mixes in alongside
* everything else a VM owns. Exists so a VM's own application-level
* conservation check (e.g. Hermes's HERMES-K, FABRIC.md §25.7, Captain
* Bob's ruling 2026-08-06) can add this as an explicit term instead of
* silently omitting word-execution heat it has no other way to see.
*
* Walks all DICTIONARY_SIZE word_slots for vm_id's quota slot; each
* resident entry contributes its cell's current heat, verified live against
* the discriminator bitmap (same self-healing pattern as
* resolve_resident_cell() -- a stale map entry contributes 0, not garbage).
*
* @param vm_id The VM whose word-execution residents to sum.
*/
uint64_t stadium_words_resident_heat(VMUuid vm_id);
/*
* stadium_words_stats - Promotion/eviction counters (same shape as the old
@@ -113,9 +152,10 @@ void stadium_word_forget(uint32_t word_id);
* admission. Eviction = this word's cell was reaped by another admission's
* eviction fallback (stadium_admit()'s density comparison), detected
* lazily via the self-healing stale check in stadium_word_dispatch(), or
* explicitly via stadium_word_forget().
* explicitly via stadium_word_forget(). Scoped to vm_id's own quota slot
* (item 4.2) -- counters are no longer system-wide.
*/
void stadium_words_stats(uint64_t *promotions, uint64_t *evictions);
void stadium_words_stats(VMUuid vm_id, uint64_t *promotions, uint64_t *evictions);
/*
* stadium_words_print_boot_diagnostics - Console output satisfying item
@@ -123,11 +163,9 @@ void stadium_words_stats(uint64_t *promotions, uint64_t *evictions);
* acceptance line. Prints promotions/evictions, then
* Σ(resident heat) + reservoir against Q48_ONE as a conservation check --
* not required by the acceptance text, but the mechanism proves nothing if
* this silently doesn't hold. The heat sum is taken over ALL resident
* Stadium cells, not scoped by owner -- correct only because vm_id is the
* sole VM with any Stadium quota today (item 0.1's Hera-only pruning); the
* per-cell owner byte is private to stadium.c and has no public accessor.
* Revisit the scoping when item 4.2 restores Hermes.
* this silently doesn't hold. The heat sum is scoped to vm_id's own quota
* (stadium_resident_sum(), item 4.2, FABRIC.md §25.5) so two VMs' checks
* close independently instead of mixing both VMs' resident heat together.
*
* @param vm_id The VM whose reservoir to read (vm_uuid_hera() today).
*/
+13
View File
@@ -358,6 +358,8 @@ typedef enum
} vm_mode_t;
#ifdef __STARKERNEL__
#include "starkernel/vm_uuid.h" /* VMUuid -- item 4.2, VM.stadium_vm_id */
/* Saved interpreter state for VM lifecycle calls (BIRTH, VM-EXEC, START).
* Dynamically allocated; grows on demand via sf_realloc. */
typedef struct {
@@ -529,6 +531,17 @@ typedef struct VM
int call_stack_cap; /**< Allocated capacity in entries */
int call_stack_max; /**< High-water mark depth (DoE metric) */
/** @} */
/** @name Stadium Identity (item 4.2, FABRIC.md §25.5)
* @{
*/
VMUuid stadium_vm_id; /**< This VM's own identity, set once at birth from
* VMRegistryEntry.vm_id. Lets vm_core.c's
* stadium_word_dispatch() call sites attribute
* word-execution heat to the VM that actually
* ran the word, instead of item 4.1's hardcoded
* vm_uuid_hera(). */
/** @} */
#endif
} VM;
Binary file not shown.
+1 -1
View File
@@ -135,7 +135,7 @@ void vm_dictionary_untrack_entry(VM *vm, DictEntry *entry) {
* is recycled below -- otherwise the next word assigned this same id
* would alias onto the forgotten word's stale cell (same failure class
* as the 2026-08-02 block_words.c aliasing bug). */
stadium_word_forget(word_id);
stadium_word_forget(vm->stadium_vm_id, word_id);
#endif
if (vm->word_id_map[word_id] == entry) {
+5
View File
@@ -475,6 +475,11 @@ CapsuleRunResult capsule_birth_baby(
return CAPSULE_RUN_ERR_STILLBORN;
}
/* item 4.2: set before the IDENTITY exec below, so any word this baby
* dispatches during her own init capsule already attributes heat to her
* own reservoir, not vm_uuid_hera()'s (item 4.1's hardcoded default). */
((VM *)new_vm)->stadium_vm_id = vm_id;
const uint8_t *payload = capsule_get_payload(cap, arena);
if (!payload) {
entry->state = VM_STATE_STILLBORN;
+204 -5
View File
@@ -44,6 +44,8 @@
#include "starkernel/capsule_loader.h"
#include "starkernel/capsule_vm_physics.h"
#include "starkernel/vm/vm_internal.h"
#include "starkernel/vm/stadium.h" /* item 4.2 -- STADIUM-* words */
#include "starkernel/vm/stadium_words.h" /* item 4.2 -- stadium_words_resident_heat() */
#include "starkernel/repl.h"
#include "starkernel/capsule_generated.h"
#include "starkernel/console.h"
@@ -1156,16 +1158,205 @@ void register_mama_forth_words(VM *vm)
vocabulary_word_definitions(vm);
}
/* ============================================================================
* Stadium Words (FABRIC.md punch list item 4.2)
*
* The entire C surface item 4.2 is permitted to add, per HERMES.md's
* language constraint: all eight operate on the CALLING VM's own identity
* (vm->stadium_vm_id) implicitly, never a FORTH-supplied vm-id. A
* stack-passed vm-id could only ever be the caller's own (redundant) or
* another VM's (stadium_admit()/stadium_evict() would refuse it via quota
* isolation, except STADIUM-RES-PUSH, which has no such guard and would be
* an outright heat-forgery primitive against another VM's reservoir).
* Conservation is the invariant this item is verified against, so implicit
* self is not an optimization -- it's the only version that can't break it.
*
* STADIUM-ADMIT does not itself pull the candidate's heat from the
* reservoir -- that's STADIUM-RES-PULL's job, composed in FORTH by the
* caller (e.g. a rewritten MSG-ALLOC): pull first, admit with the pulled
* amount, and STADIUM-RES-PUSH it back if admission refuses. Mirrors
* stadium_words.c's C-side Option B starter-grant pattern, but the
* composition itself lives in StarForth, not here, per HERMES.md.
* ============================================================================ */
/**
* @brief STADIUM-ADMIT ( identity heat behaviour -- cell | -1 )
* Admits a mass-1 patron into the calling VM's own Stadium quota.
* `behaviour` must be a valid StadiumBehaviour tag (0..3); anything else
* refuses without calling stadium_admit() at all. `contains` is always
* explicitly STADIUM_CONTAINS_NONE -- stadium_admit()'s own doc warns a
* zero-initialized `contains` reads as "contains Hera" (index 0) and
* permanently blocks eviction, so this is never left to a zero-fill.
*/
static void mama_word_stadium_admit(VM *vm)
{
cell_t behaviour_cell, heat_cell, identity_cell;
StadiumPatronHeader candidate;
uint8_t *raw = (uint8_t *)&candidate;
size_t i;
size_t idx;
if (vm->dsp < 2) { vm->error = 1; return; }
behaviour_cell = vm_pop(vm);
heat_cell = vm_pop(vm);
identity_cell = vm_pop(vm);
if (behaviour_cell < STADIUM_BEHAVIOUR_MIGRATE || behaviour_cell > STADIUM_BEHAVIOUR_COOL) {
vm_push(vm, (cell_t)-1);
return;
}
for (i = 0; i < sizeof(candidate); i++) raw[i] = 0;
candidate.identity = (uint64_t)identity_cell;
candidate.heat = (uint64_t)heat_cell;
candidate.ttl = 0;
candidate.link = 0;
candidate.contains = STADIUM_CONTAINS_NONE;
candidate.mass = 1;
candidate.flags = 0;
candidate.behaviour = (uint8_t)behaviour_cell;
idx = stadium_admit(vm->stadium_vm_id, &candidate);
vm_push(vm, (idx == STADIUM_CELL_NONE) ? (cell_t)-1 : (cell_t)idx);
}
/**
* @brief STADIUM-EVICT ( cell -- flag )
* Reaps the patron header at `cell`. flag is FORTH true (-1) on success,
* false (0) if refused (out of range, not resident, pinned, or contains-
* gated) -- stadium_evict()'s own refusal set, unchanged here.
*/
static void mama_word_stadium_evict(VM *vm)
{
cell_t cell_cell;
if (vm->dsp < 0) { vm->error = 1; return; }
cell_cell = vm_pop(vm);
if (cell_cell < 0) {
vm_push(vm, (cell_t)0);
return;
}
vm_push(vm, (stadium_evict((size_t)cell_cell) == 0) ? (cell_t)-1 : (cell_t)0);
}
/**
* @brief STADIUM-RES@ ( -- heat )
* Read-only peek at the calling VM's own reservoir balance (Q48.16).
*/
static void mama_word_stadium_res_fetch(VM *vm)
{
vm_push(vm, (cell_t)stadium_reservoir_peek(vm->stadium_vm_id));
}
/**
* @brief STADIUM-WORD-HEAT ( -- heat )
* Sum of heat held by the calling VM's own word-execution residents
* (item 4.1's cells) -- the term a VM's own application-level conservation
* check (e.g. Hermes's HERMES-K) needs to close exactly, since word patrons
* are otherwise invisible to FORTH (FABRIC.md §25.7, ruling 2026-08-06).
*/
static void mama_word_stadium_word_heat(VM *vm)
{
vm_push(vm, (cell_t)stadium_words_resident_heat(vm->stadium_vm_id));
}
/**
* @brief STADIUM-RES-PULL ( qty -- heat )
* Pulls up to `qty` (Q48.16) from the calling VM's own reservoir. Returns
* the amount actually pulled, which may be less than requested -- never
* negative, never invents heat, mirrors stadium_reservoir_pull()'s own
* clamping exactly.
*/
static void mama_word_stadium_res_pull(VM *vm)
{
cell_t qty_cell;
if (vm->dsp < 0) { vm->error = 1; return; }
qty_cell = vm_pop(vm);
if (qty_cell < 0) {
vm_push(vm, (cell_t)0);
return;
}
vm_push(vm, (cell_t)stadium_reservoir_pull(vm->stadium_vm_id, (uint64_t)qty_cell));
}
/**
* @brief STADIUM-RES-PUSH ( heat -- )
* Credits `heat` (Q48.16) back into the calling VM's own reservoir -- the
* other half of every reservoir transfer (cooling, refused-admission
* rollback, or a departing patron's remaining heat after eviction).
*/
static void mama_word_stadium_res_push(VM *vm)
{
cell_t heat_cell;
if (vm->dsp < 0) { vm->error = 1; return; }
heat_cell = vm_pop(vm);
if (heat_cell < 0) return;
stadium_reservoir_push(vm->stadium_vm_id, (uint64_t)heat_cell);
}
/**
* @brief STADIUM-HEAT@ ( cell -- heat )
* Reads a resident cell's own heat. Requires the cell to be resident and
* owned by the calling VM's own quota -- returns 0 otherwise (out of range,
* not resident, or belongs to a different VM).
*/
static void mama_word_stadium_heat_fetch(VM *vm)
{
cell_t cell_cell;
if (vm->dsp < 0) { vm->error = 1; return; }
cell_cell = vm_pop(vm);
if (cell_cell < 0) {
vm_push(vm, (cell_t)0);
return;
}
vm_push(vm, (cell_t)stadium_cell_heat_get(vm->stadium_vm_id, (size_t)cell_cell));
}
/**
* @brief STADIUM-HEAT! ( new-heat cell -- )
* Writes a resident cell's own heat, reconciling the reservoir delta
* atomically in C (pulls on an increase, refusing silently if the
* calling VM's reservoir can't cover it; pushes back on a decrease).
* Requires the cell to be resident and owned by the calling VM's own
* quota -- silently refused otherwise, same as every other write here.
*/
static void mama_word_stadium_heat_store(VM *vm)
{
cell_t cell_cell, new_heat_cell;
if (vm->dsp < 1) { vm->error = 1; return; }
cell_cell = vm_pop(vm);
new_heat_cell = vm_pop(vm);
if (cell_cell < 0 || new_heat_cell < 0) return;
(void)stadium_cell_heat_set(vm->stadium_vm_id, (size_t)cell_cell, (uint64_t)new_heat_cell);
}
/**
* register_child_vm_words - Register the minimal word set needed by child VMs.
*
* Child VMs are not bootstrapped through sk_vm_bootstrap_parity, so they
* do not get register_mama_forth_words(). They only need STOP (self-halt)
* and EXEC (load a capsule). Keeping the registrations here — in the same
* translation unit as the word functions — avoids cross-TU function-pointer
* loads that produce R_X86_64_REX_GOTPCRELX relocations; those are not
* relaxed by the PE32+ linker, causing the function code bytes to be read
* as the pointer value instead of the actual address.
* and EXEC (load a capsule) -- plus, as of item 4.2, the eight STADIUM-*
* primitives Hermes needs to migrate her message/channel lifecycle onto the
* Stadium. Keeping the registrations here — in the same translation unit
* as the word functions — avoids cross-TU function-pointer loads that
* produce R_X86_64_REX_GOTPCRELX relocations; those are not relaxed by the
* PE32+ linker, causing the function code bytes to be read as the pointer
* value instead of the actual address.
*
* Deliberately NOT added to register_mama_forth_words(): that would put
* these words in Hera's own dictionary too and move dict_hash off item
* 4.1's baseline (0x3d4e1daf289da94f) -- a deliberate baseline change to
* state this item does not make as a side effect.
*/
void register_child_vm_words(VM *vm)
{
@@ -1173,6 +1364,14 @@ void register_child_vm_words(VM *vm)
register_word(vm, "EXEC", mama_word_exec);
register_word(vm, "VM-EXEC", mama_word_vm_exec);
register_word(vm, "VM-CALL", mama_word_vm_call);
register_word(vm, "STADIUM-ADMIT", mama_word_stadium_admit);
register_word(vm, "STADIUM-EVICT", mama_word_stadium_evict);
register_word(vm, "STADIUM-RES@", mama_word_stadium_res_fetch);
register_word(vm, "STADIUM-RES-PULL", mama_word_stadium_res_pull);
register_word(vm, "STADIUM-RES-PUSH", mama_word_stadium_res_push);
register_word(vm, "STADIUM-HEAT@", mama_word_stadium_heat_fetch);
register_word(vm, "STADIUM-HEAT!", mama_word_stadium_heat_store);
register_word(vm, "STADIUM-WORD-HEAT", mama_word_stadium_word_heat);
}
#endif /* __STARKERNEL__ */
+119
View File
@@ -165,6 +165,30 @@ static void print_uint(const char *label, uint64_t value) {
console_println(buf);
}
#ifdef STARFORTH_ENABLE_VM
/**
* @brief Verify a known word is still reachable in a VM's dictionary.
*
* Diagnostic-only: confirms @c vm_find_word() can still walk the chain
* from @c vm->latest to a word defined early in Hermes's capsule
* (MSG-COOL-ALL, block 4108). Added after item 4.2's self-test bisection
* found an amd64-only in-place corruption of an existing dictionary entry
* during MSG-DELIVER-ALL -- here/latest stay unchanged (no reallocation),
* so a lookup is a cheap signal. NOTE: an earlier version of this comment
* attributed the corruption to GDB perturbing execution timing; that is
* unconfirmed and more likely just a parity/dict-hash boot-gate failure
* triggered by the debugger session itself (a software breakpoint's 0xCC
* patch landing in memory the loader then overwrote) -- don't propagate
* "timing-sensitive" as an established finding.
*/
static void hermes_dict_check(VM *hermes_vm, const char *checkpoint) {
DictEntry *e = vm_find_word(hermes_vm, "MSG-COOL-ALL", 12);
console_puts(" Dict-check ");
console_puts(checkpoint);
console_println(e ? ": OK" : ": FAIL (MSG-COOL-ALL unreachable)");
}
#endif
/**
* @brief Print a boot-information summary from the UEFI memory map to the console.
*
@@ -648,6 +672,101 @@ static void kernel_main_deep(BootInfo *boot_info) {
}
}
/* item 4.2 self-test: a REAL birth (not synthetic, unlike 4.1a's --
* this exercises capsules/hermes/init.4th's actual migrated code),
* exercised then killed again so the resting boot state stays
* Hera-alone, per item 0.1's intent. Diagnostic only -- production
* boot still never auto-births Hermes (init.4th's BIRTH stays
* commented out). */
console_println("Hermes 4.2 migration self-test: birthing...");
vm_interpret(mama, "S\" Hermes\" BIRTH");
{
VMRegistryEntry entry;
if (capsule_vm_find_by_name_nocase("Hermes", &entry) == 0 &&
entry.state == VM_STATE_LIVE) {
VM *hermes_vm = (VM *)entry.vm_ptr;
console_println("Hermes 4.2 self-test: exercising migrated words...");
hermes_dict_check(hermes_vm, "at self-test start");
hermes_vm->error = 0;
vm_interpret(hermes_vm, "CD-INIT");
print_uint(" DBG err after CD-INIT=", (uint64_t)hermes_vm->error);
hermes_dict_check(hermes_vm, "after CD-INIT");
hermes_vm->error = 0;
vm_interpret(hermes_vm, "HERMES-MSG-TEST . CR");
print_uint(" DBG err after MSG-TEST=", (uint64_t)hermes_vm->error);
hermes_dict_check(hermes_vm, "after MSG-TEST");
hermes_vm->error = 0;
vm_interpret(hermes_vm, "HERMES-STATUS");
print_uint(" DBG err after STATUS=", (uint64_t)hermes_vm->error);
hermes_dict_check(hermes_vm, "after STATUS");
hermes_vm->error = 0;
vm_interpret(hermes_vm, "MSG-DELIVER-ALL");
print_uint(" DBG err after DELIVER-ALL=", (uint64_t)hermes_vm->error);
hermes_dict_check(hermes_vm, "after DELIVER-ALL");
hermes_vm->error = 0;
vm_interpret(hermes_vm, "MSG-REDELIVER-NACKED");
print_uint(" DBG err after REDELIVER=", (uint64_t)hermes_vm->error);
hermes_vm->error = 0;
vm_interpret(hermes_vm, "MSG-COOL-ALL");
print_uint(" DBG err after MSG-COOL-ALL=", (uint64_t)hermes_vm->error);
hermes_vm->error = 0;
vm_interpret(hermes_vm, "MSG-REAP");
print_uint(" DBG err after MSG-REAP=", (uint64_t)hermes_vm->error);
hermes_vm->error = 0;
vm_interpret(hermes_vm, "CH-COOL-ALL CH-REAP-SAFE");
print_uint(" DBG err after CH-COOL/REAP=", (uint64_t)hermes_vm->error);
hermes_vm->error = 0;
vm_interpret(hermes_vm, "Q.1 3 / COMMON-CH @ CH-HEAT!");
print_uint(" DBG err after CH-HEAT!=", (uint64_t)hermes_vm->error);
hermes_vm->error = 0;
vm_interpret(hermes_vm, "HERMES-STATUS");
print_uint(" DBG err after TICK=", (uint64_t)hermes_vm->error);
hermes_vm->error = 0;
console_puts("Hermes 4.2 self-test: HERMES-K=");
vm_interpret(hermes_vm, "HERMES-K .");
console_println("");
print_uint(" DBG err after HERMES-K=", (uint64_t)hermes_vm->error);
/* Side-by-side with the Stadium's own view, same checkpoint --
* if these two disagree, HERMES-K's FORTH-side arena scan is
* seeing different residents than stadium_resident_sum()'s
* ownership+bitmap view (a real, separate finding, not the
* Q.SLOT admission-heat fix's job to explain). */
print_uint(" DBG stadium_resident_sum(Hermes)=", stadium_resident_sum(entry.vm_id));
print_uint(" DBG stadium_reservoir_peek(Hermes)=", stadium_reservoir_peek(entry.vm_id));
/* item 4.2 Done-when: "a resident cell's evict-credit landing
* in the correct VM's reservoir ... not just asserted from
* reading the code" -- explicitly evict the common channel
* (CH-FREE-NODE -> STADIUM-EVICT), a known resident from
* CD-INIT's own COMMON-INIT, same credit path stadium_admit()'s
* density-fallback eviction uses. resident_sum dropping and
* reservoir rising by the same amount is the proof: the freed
* cell's heat landed back in Hermes's own reservoir, not lost
* or credited to Hera. */
console_println("Hermes 4.2 self-test: before eviction:");
print_uint(" Hermes resident_sum=", stadium_resident_sum(entry.vm_id));
print_uint(" Hermes reservoir=", stadium_reservoir_peek(entry.vm_id));
console_println("Hermes 4.2 self-test: forcing an explicit eviction (COMMON-CH)...");
hermes_vm->error = 0;
vm_interpret(hermes_vm, "COMMON-CH @ . CR");
print_uint(" DBG err after COMMON-CH@=", (uint64_t)hermes_vm->error);
hermes_vm->error = 0;
vm_interpret(hermes_vm, "COMMON-CH @ CH-FREE-NODE");
print_uint(" DBG err after CH-FREE-NODE=", (uint64_t)hermes_vm->error);
console_println("Hermes 4.2 self-test: after eviction:");
print_uint(" Hermes resident_sum=", stadium_resident_sum(entry.vm_id));
print_uint(" Hermes reservoir=", stadium_reservoir_peek(entry.vm_id));
/* item 4.2 Done-when: both VMs' conservation checks close
* independently -- Hermes's own resident+reservoir sum first,
* then Hera's again (unaffected by Hermes's activity above). */
stadium_words_print_boot_diagnostics(entry.vm_id);
vm_interpret(mama, "S\" Hermes\" KILL");
console_println("Hermes 4.2 self-test: killed, resting state restored");
stadium_words_print_boot_diagnostics(vm_uuid_hera());
} else {
console_println("Hermes 4.2 self-test: birth registry lookup FAILED");
}
}
/*
* Runtime --doe flag: inject "EXEC-DOE BYE" if requested via boot args.
* Checked before SK_STARTUP_FORTH so a runtime --doe takes precedence.
@@ -271,6 +271,7 @@ int sk_vm_bootstrap_parity(ParityPacket *out) {
* capsule_birth_baby() which never calls vm_init_with_host(). */
capsule_vm_hooks_register();
capsule_vm_registry_init(vm); /* establishes [Hera] console prefix */
vm->stadium_vm_id = vm_uuid_hera(); /* item 4.2 */
vm_physics_init(vm_uuid_hera()); /* Hera: the fleet's root, seeded Q48_ONE */
capsule_run_log_init();
register_mama_forth_words(vm); /* BIRTH KILL START STOP USE + capsule words */
+67
View File
@@ -358,6 +358,10 @@ size_t stadium_admit(VMUuid vm_id, const StadiumPatronHeader *candidate) {
stadium_quotas[slot].free_head = link_to_size(stadium_cell_array[idx].header.link);
stadium_cell_array[idx].header = *candidate;
bitmap_set(idx);
/* Item 4.2 fix (§25.7): record ownership so stadium_evict()'s
* reservoir credit and free-list return land on the VM that actually
* admitted this patron, not whatever owner[idx] held at boot. */
stadium_owner[idx] = (uint8_t)slot;
return idx;
}
@@ -395,6 +399,11 @@ size_t stadium_admit(VMUuid vm_id, const StadiumPatronHeader *candidate) {
stadium_quotas[slot].free_head = link_to_size(stadium_cell_array[idx].header.link);
stadium_cell_array[idx].header = *candidate;
bitmap_set(idx);
/* Same fix as the free-list-pop path above -- stadium_evict() just wrote
* owner[idx] = slot as part of reaping least_dense_index, so this is
* currently a no-op in practice, but it must not be assumed to stay a
* no-op: this is the correctness statement, not a redundant write. */
stadium_owner[idx] = (uint8_t)slot;
return idx;
}
@@ -453,6 +462,45 @@ int stadium_grant_quota(VMUuid new_vm_id, VMUuid from_vm_id) {
return 0;
}
/* Shared by stadium_cell_heat_get()/_set(): resident AND owned by vm_id's
* own quota slot. Returns the quota slot on success, -1 on any refusal. */
static int owned_resident_slot(VMUuid vm_id, size_t cell_index) {
int slot = quota_slot_for_vm(vm_id);
if (slot < 0) return -1;
if (cell_index >= stadium_ncells) return -1;
if (!bitmap_get(cell_index)) return -1;
if (stadium_owner[cell_index] != (uint8_t)slot) return -1;
return slot;
}
uint64_t stadium_cell_heat_get(VMUuid vm_id, size_t cell_index) {
if (owned_resident_slot(vm_id, cell_index) < 0) return 0;
return stadium_cell_array[cell_index].header.heat;
}
int stadium_cell_heat_set(VMUuid vm_id, size_t cell_index, uint64_t new_heat) {
int slot = owned_resident_slot(vm_id, cell_index);
uint64_t old_heat, delta, pulled;
if (slot < 0) return -1;
old_heat = stadium_cell_array[cell_index].header.heat;
if (new_heat == old_heat) return 0;
if (new_heat > old_heat) {
delta = new_heat - old_heat;
pulled = (delta > stadium_quotas[slot].reservoir) ? stadium_quotas[slot].reservoir : delta;
if (pulled < delta) return -1; /* insufficient -- no partial credit, no mutation */
stadium_quotas[slot].reservoir -= pulled;
} else {
delta = old_heat - new_heat;
stadium_quotas[slot].reservoir += delta;
}
stadium_cell_array[cell_index].header.heat = new_heat;
return 0;
}
uint64_t stadium_reservoir_pull(VMUuid vm_id, uint64_t amount) {
int slot = quota_slot_for_vm(vm_id);
uint64_t pulled;
@@ -478,6 +526,25 @@ uint64_t stadium_reservoir_peek(VMUuid vm_id) {
return stadium_quotas[slot].reservoir;
}
int stadium_quota_slot_for_vm(VMUuid vm_id) {
return quota_slot_for_vm(vm_id);
}
uint64_t stadium_resident_sum(VMUuid vm_id) {
int slot = quota_slot_for_vm(vm_id);
uint64_t sum = 0;
size_t i;
if (slot < 0) return 0;
for (i = 0; i < stadium_ncells; i++) {
if (!bitmap_get(i)) continue;
if (stadium_owner[i] != (uint8_t)slot) continue;
sum += stadium_cell_array[i].header.heat;
}
return sum;
}
/*
* FABRIC.md item 3.6 / item 4.1: see stadium.h's doc. Idempotent via the
* item-3.1 discriminator bitmap -- if cell 0 already reads as resident,
+100 -39
View File
@@ -40,25 +40,37 @@
* 2026-08-05: no DictEntry field). `last_decay_tick` is this layer's own
* bookkeeping, separate from DictEntry.physics.last_decay_tick -- that field
* belongs to execution_heat's decay, which item 4.1 does not touch.
*
* item 4.2 fix (FABRIC.md §25.5): keyed by [quota slot][word_id], not just
* word_id. word_id is assigned per-VM (vm->next_word_id in
* dictionary_management.c), not globally unique -- a single shared
* word_id -> cell_index map let two VMs' independently-numbered word_ids
* (e.g. both VMs' own "DUP") alias onto the same slot, so one VM's dispatch
* could cool/heat-pump a cell it did not own and credit/debit the wrong
* VM's reservoir. Exposed only because item 4.2 restored a second VM
* (Hermes) with her own dictionary; invisible with Hera alone.
*/
typedef struct {
size_t cell_index; /* STADIUM_CELL_NONE if not resident */
uint64_t last_decay_tick;
} StadiumWordSlot;
static StadiumWordSlot word_slots[DICTIONARY_SIZE];
static StadiumWordSlot word_slots[STADIUM_MAX_VM_COUNT][DICTIONARY_SIZE];
static int words_initialized = 0;
static uint64_t stat_promotions = 0;
static uint64_t stat_evictions = 0;
static uint64_t stat_promotions[STADIUM_MAX_VM_COUNT];
static uint64_t stat_evictions[STADIUM_MAX_VM_COUNT];
void stadium_words_init(void) {
int slot;
uint32_t i;
for (i = 0; i < DICTIONARY_SIZE; i++) {
word_slots[i].cell_index = STADIUM_CELL_NONE;
word_slots[i].last_decay_tick = 0;
for (slot = 0; slot < STADIUM_MAX_VM_COUNT; slot++) {
for (i = 0; i < DICTIONARY_SIZE; i++) {
word_slots[slot][i].cell_index = STADIUM_CELL_NONE;
word_slots[slot][i].last_decay_tick = 0;
}
stat_promotions[slot] = 0;
stat_evictions[slot] = 0;
}
stat_promotions = 0;
stat_evictions = 0;
words_initialized = 1;
}
@@ -68,6 +80,29 @@ static int cell_is_resident(size_t idx) {
return (bm[idx / 8u] >> (idx % 8u)) & 1u;
}
/*
* word_dispatch_pull - Reservoir pull for word-execution admission, clamped
* to leave a floor for application-level use (FABRIC.md §25.5/§25.7,
* Captain Bob's ruling 2026-08-06). Without this, stadium_word_dispatch()
* pulling STADIUM_WORD_HEAT_QUANTUM on every dispatch -- not just the first
* admission of a given word -- exhausts a VM's entire reservoir within
* roughly 32 total dispatches (65536 / 2048), starving any item-4.2-style
* application economy sharing the same VM's reservoir before it gets a
* chance to pull anything. The floor is Q48_ONE / 3, the same "VM-COUNT=3
* fair share" reasoning capsules/hermes/init.4th's COMMON-CH floor already
* uses -- not a new invented number. Application-level pulls
* (stadium_reservoir_pull() called directly, e.g. via STADIUM-RES-PULL) are
* NOT floored -- only word-execution admission respects this ceiling on
* its own consumption.
*/
static uint64_t word_dispatch_pull(VMUuid vm_id, uint64_t want) {
uint64_t available = stadium_reservoir_peek(vm_id);
uint64_t floor = Q48_ONE / 3;
uint64_t pullable = (available > floor) ? (available - floor) : 0;
uint64_t capped = (want < pullable) ? want : pullable;
return stadium_reservoir_pull(vm_id, capped);
}
/*
* resolve_resident_cell - Self-healing lookup (advisor-flagged reverse
* coherence gap): the map may claim word_id is resident at a cell that was
@@ -77,36 +112,59 @@ static int cell_is_resident(size_t idx) {
* no new coupling from stadium.c into this file. A stale mapping is cleared
* and counted as an eviction on discovery.
*/
static size_t resolve_resident_cell(uint32_t word_id) {
size_t cell = word_slots[word_id].cell_index;
static size_t resolve_resident_cell(int slot, uint32_t word_id) {
size_t cell = word_slots[slot][word_id].cell_index;
StadiumCell *cells;
if (cell == STADIUM_CELL_NONE) return STADIUM_CELL_NONE;
if (cell >= stadium_cell_count() || !cell_is_resident(cell)) {
word_slots[word_id].cell_index = STADIUM_CELL_NONE;
stat_evictions++;
word_slots[slot][word_id].cell_index = STADIUM_CELL_NONE;
stat_evictions[slot]++;
return STADIUM_CELL_NONE;
}
cells = stadium_cells();
if (cells[cell].header.identity != (uint64_t)word_id) {
word_slots[word_id].cell_index = STADIUM_CELL_NONE;
stat_evictions++;
word_slots[slot][word_id].cell_index = STADIUM_CELL_NONE;
stat_evictions[slot]++;
return STADIUM_CELL_NONE;
}
return cell;
}
uint64_t stadium_words_resident_heat(VMUuid vm_id) {
int slot;
uint32_t i;
uint64_t sum = 0;
StadiumCell *cells;
if (!words_initialized) return 0;
slot = stadium_quota_slot_for_vm(vm_id);
if (slot < 0) return 0;
cells = stadium_cells();
for (i = 0; i < DICTIONARY_SIZE; i++) {
size_t cell = resolve_resident_cell(slot, i);
if (cell == STADIUM_CELL_NONE) continue;
sum += cells[cell].header.heat;
}
return sum;
}
void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ticks) {
int slot;
size_t cell;
if (!words_initialized) return;
if (word_id == WORD_ID_INVALID || word_id >= DICTIONARY_SIZE) return;
cell = resolve_resident_cell(word_id);
slot = stadium_quota_slot_for_vm(vm_id);
if (slot < 0) return;
cell = resolve_resident_cell(slot, word_id);
if (cell != STADIUM_CELL_NONE) {
StadiumPatronHeader *h = &stadium_cells()[cell].header;
uint64_t elapsed = heartbeat_ticks - word_slots[word_id].last_decay_tick;
uint64_t elapsed = heartbeat_ticks - word_slots[slot][word_id].last_decay_tick;
if (elapsed > 0) {
/* Redirected Loop #3 (§17.7): a FRACTION of the cell's own
@@ -120,17 +178,17 @@ void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ti
h->heat -= cooled;
stadium_reservoir_push(vm_id, cooled);
}
word_slots[word_id].last_decay_tick = heartbeat_ticks;
word_slots[slot][word_id].last_decay_tick = heartbeat_ticks;
}
h->heat += stadium_reservoir_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
h->heat += word_dispatch_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
return;
}
/* Not resident: Option B starter-grant admission (§17.7). execution_heat
* plays no role -- density is decided entirely by the pulled quantum. */
{
uint64_t pulled = stadium_reservoir_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
uint64_t pulled = word_dispatch_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
StadiumPatronHeader candidate;
uint8_t *raw = (uint8_t *)&candidate;
size_t i;
@@ -152,30 +210,36 @@ void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ti
return;
}
word_slots[word_id].cell_index = idx;
word_slots[word_id].last_decay_tick = heartbeat_ticks;
stat_promotions++;
word_slots[slot][word_id].cell_index = idx;
word_slots[slot][word_id].last_decay_tick = heartbeat_ticks;
stat_promotions[slot]++;
}
}
void stadium_word_forget(uint32_t word_id) {
void stadium_word_forget(VMUuid vm_id, uint32_t word_id) {
int slot;
size_t cell;
if (!words_initialized) return;
if (word_id == WORD_ID_INVALID || word_id >= DICTIONARY_SIZE) return;
cell = resolve_resident_cell(word_id);
slot = stadium_quota_slot_for_vm(vm_id);
if (slot < 0) return;
cell = resolve_resident_cell(slot, word_id);
if (cell == STADIUM_CELL_NONE) return;
if (stadium_evict(cell) == 0) {
word_slots[word_id].cell_index = STADIUM_CELL_NONE;
stat_evictions++;
word_slots[slot][word_id].cell_index = STADIUM_CELL_NONE;
stat_evictions[slot]++;
}
}
void stadium_words_stats(uint64_t *promotions, uint64_t *evictions) {
if (promotions) *promotions = stat_promotions;
if (evictions) *evictions = stat_evictions;
void stadium_words_stats(VMUuid vm_id, uint64_t *promotions, uint64_t *evictions) {
int slot = stadium_quota_slot_for_vm(vm_id);
if (promotions) *promotions = (slot >= 0) ? stat_promotions[slot] : 0;
if (evictions) *evictions = (slot >= 0) ? stat_evictions[slot] : 0;
}
/* Freestanding: no libc printf. Prints an unsigned decimal, no leading
@@ -197,19 +261,16 @@ static void console_put_u64(uint64_t v) {
void stadium_words_print_boot_diagnostics(VMUuid vm_id) {
uint64_t promotions = 0, evictions = 0;
uint64_t resident_sum = 0;
uint64_t resident_sum;
uint64_t reservoir;
size_t ncells = stadium_cell_count();
size_t i;
stadium_words_stats(&promotions, &evictions);
stadium_words_stats(vm_id, &promotions, &evictions);
for (i = 0; i < ncells; i++) {
if (cell_is_resident(i)) {
resident_sum += stadium_cells()[i].header.heat;
}
}
reservoir = stadium_reservoir_peek(vm_id);
/* item 4.2 fix (FABRIC.md §25.5): filtered per-VM -- with two VMs
* holding quotas, summing every resident cell regardless of owner
* (the pre-4.2 behavior) mixed both VMs' conservation totals together. */
resident_sum = stadium_resident_sum(vm_id);
reservoir = stadium_reservoir_peek(vm_id);
console_puts("Stadium words: promotions=");
console_put_u64(promotions);
+6 -5
View File
@@ -684,9 +684,10 @@ void execute_colon_word(VM* vm)
/* item 4.1, FABRIC.md §17.7: feed the Stadium's independent
* conserved heat wire. execution_heat above is untouched by
* this call. vm_uuid_hera() is hardcoded here -- Tripod is
* pruned to Hera alone (item 0.1); revisit at item 4.2. */
stadium_word_dispatch(vm_uuid_hera(), w->word_id, vm->heartbeat.tick_count);
* this call. item 4.2: dispatching VM's own identity, not the
* item-4.1 hardcoded vm_uuid_hera() -- refused harmlessly by
* stadium_admit() for any VM without a granted quota. */
stadium_word_dispatch(vm->stadium_vm_id, w->word_id, vm->heartbeat.tick_count);
uint32_t word_id = w->word_id;
if (word_id < DICTIONARY_SIZE)
@@ -881,7 +882,7 @@ void vm_interpret_word(VM* vm, const char* word_str, size_t len)
entry->physics.last_decay_ns = lookup_ns;
physics_execution_heat_increment(entry);
stadium_word_dispatch(vm_uuid_hera(), entry->word_id, vm->heartbeat.tick_count);
stadium_word_dispatch(vm->stadium_vm_id, entry->word_id, vm->heartbeat.tick_count);
if (canon && canon != entry)
{
/* Apply decay to canonical entry as well */
@@ -892,7 +893,7 @@ void vm_interpret_word(VM* vm, const char* word_str, size_t len)
canon->physics.last_decay_ns = lookup_ns;
physics_execution_heat_increment(canon);
stadium_word_dispatch(vm_uuid_hera(), canon->word_id, vm->heartbeat.tick_count);
stadium_word_dispatch(vm->stadium_vm_id, canon->word_id, vm->heartbeat.tick_count);
physics_metadata_touch(canon, canon->execution_heat, lookup_ns);
}
sf_mutex_unlock(&vm->dict_lock);