# Artemis BAM — Phase 1 Acceptance Report **Date:** 2026-07-03 **Branch:** `lithosananke` **Commits:** `ed05db25` (implementation) · `ff47e2ec` (acceptance logs) **Author:** Captain Bob / Claude Code --- ## Overview This report documents the Phase 1 implementation of the Artemis Block Allocation Manager (BAM) and its three-arch QEMU acceptance. The work replaces the previous integer `ART-K` counter with a proper Q48.16 block heat system, completing the compudynamic block lifecycle in Artemis. All implementation is in StarForth dialect. No C was touched. --- ## What Was Built ### Q48.16 Block Heat Arena Replaced `VARIABLE ART-K` (integer counter, not thermodynamic) with a full per-block Q48.16 heat array: ```forth CREATE BLK-HEAT ART-DATA-BLKS CELLS ALLOT \ 22998 entries × CELL bytes ``` Each slot tracks the thermal mass of one data block (index `n` → LBN `ART-DATA-LBN + n`). ### Block Lifecycle | Event | Heat action | |-------|-------------| | `BLK-ALLOC` | Set `BLK-HEAT[n] = Q.1` (born hot) | | `BLK-FETCH` (data LBN) | Refresh `BLK-HEAT[n] = Q.1` (access reheats) | | `ART-COOL` | Multiply all live slots by `Q-DECAY` (≈ 0.9950 per tick) | | `ART-REAP` | Free any allocated block whose heat has decayed to zero | | `BLK-FREE` | Set `BLK-HEAT[n] = 0` immediately | `Q-DECAY = 65208` (Q48.16 ≈ 0.9950). A block untouched for ~200 ticks cools to zero and is reaped automatically by `ART-REAP`. ### New Words | Word | Stack | Description | |------|-------|-------------| | `BLK-HEAT@` | `( n -- q48 )` | Read heat for data block index n | | `BLK-HEAT!` | `( q48 n -- )` | Write heat for data block index n | | `LBN>IDX` | `( lbn -- n )` | Convert data LBN to heat array index | | `ART-K-TOTAL` | `( -- q48 )` | Sum all block heats (fleet K observability) | | `ART-COOL` | `( -- )` | Apply Q-DECAY to all live block heat slots | | `ART-REAP` | `( -- )` | Free allocated blocks with heat = 0 | | `ART-TICK` | `( -- )` | `ART-COOL ART-REAP` — one lifecycle tick | | `CD-INIT` | `( -- )` | Hera-callable init: zero BLK-HEAT, log ready | ### Modified Words - **`BLK-ALLOC`** — now sets `BLK-HEAT[n] = Q.1` on allocation - **`BLK-FREE`** — now sets `BLK-HEAT[n] = 0` on release - **`BLK-FETCH`** — now refreshes `BLK-HEAT[n] = Q.1` for data block accesses - **`ART-INIT`** — now zeroes the entire `BLK-HEAT` arena before boot detection - **`ART-STATUS`** — now prints `K-total=` via `ART-K-TOTAL Q.PRINT` - **`ART-SELF-TEST`** — now prints K-total before and after alloc/free ### init.4th (Block 2049) Hera now calls `CD-INIT` on each baby VM immediately after BIRTH: ```forth S" Artemis" BIRTH S" CD-INIT" S" Artemis" VM-EXEC S" Hermes" BIRTH S" CD-INIT" S" Hermes" VM-EXEC ``` `CD-INIT` zeroes `BLK-HEAT` at ground-state join (heat does not persist across boots; disk content does). --- ## Block Inventory All blocks are in `capsules/artemis/init.4th`. Block numbers are capsule-local annotations; they do not map to shared ramdrive LBNs. | Block | Status | Purpose | |-------|--------|---------| | 4110 | Modified | Removed `ART-K`; added `Q-DECAY = 65208` | | 4111 | Unchanged | LE32!/LE32@ little-endian I/O | | 4112 | Unchanged | SHL1N/SHR1N/BIT-TEST/BIT-SET/BIT-CLR | | 4113 | Unchanged | Free-map (FM-ADDR-BIT/FM-TEST/FM-SET/FM-CLR) | | 4122 | Modified | BLK-ALLOC/BLK-FREE with heat lifecycle | | 4123 | Unchanged | ART-MAGIC!/ART-MAGIC? | | 4124 | Modified | BLK-FETCH with heat bump; ART-BOOT-DETECT | | 4125 | Unchanged | ART-HDR-WRITE | | 4126 | Unchanged | ART-FORMAT/ART-RESUME/ART-HALT-UNRECOG | | 4127 | Modified | ART-STATUS with Q.PRINT; WELCOME | | 4128 | Unchanged | LE64!/LE64@ | | 4130 | Unchanged | ART-BLANK? | | 4131 | Modified | ART-INIT zeroes BLK-HEAT arena | | 4132 | Modified | ART-SELF-TEST with K-total display | | 4133 | Unchanged | Entry block (ART-INIT ART-BOOT-ENTRY WELCOME) | | 4134 | Unchanged | ART-WRITE-TEST | | 4135 | Unchanged | ART-READ-TEST | | 4136 | Unchanged | ART-BOOT-ENTRY dispatch | | **4137** | **New** | BLK-HEAT arena (`CREATE BLK-HEAT`) | | **4138** | **New** | BLK-HEAT@/BLK-HEAT!/LBN>IDX | | 4139 | **New** | ART-K-TOTAL/ART-COOL | | 4140 | **New** | ART-REAP/ART-TICK | | **4141** | **New** | CD-INIT (Hera-callable ground-state join) | --- ## Design Decisions ### ART-K-TOTAL is observability only `ART-K-TOTAL` sums all block heats and is displayed in `ART-STATUS`. It is **not** wired into `K-FLEET` in `fleet-k.4th`. `K-FLEET` currently uses `2 K-LOCAL@` (rolling-window average) for Artemis. Replacing this with `ART-K-TOTAL` would break `K-CONSERVED?` at boot: a fresh boot has zero allocated blocks, so `ART-K-TOTAL = 0`, making fleet K ≈ 2/3 instead of 1.0. The correct fix (Logical BAM) requires normalizing per-block heat so that the allocated pool represents exactly `Q.1` of Artemis's share. That is deferred. ### ART-TICK is manual `ART-TICK` is defined but not connected to any automatic heartbeat loop. Blocks cool only when `ART-TICK` is called explicitly. Automatic integration with a compudynamic tick loop is future work (same Logical BAM chapter). ### No integer ART-K The old `VARIABLE ART-K` (simple integer counter, not thermodynamic) is removed. `ART-K-TOTAL` is Q48.16 and reflects actual thermal state. --- ## Acceptance Results Three-arch QEMU run, 2026-07-03. Logs in `logs/20260703-15*/`. | ISA | Disk | Artemis boot test | Tripod (6/6) | |-----|------|-------------------|--------------| | amd64 | virtio-blk (resume) | PASS: persist-read | ✅ | | aarch64 | virtio-blk (resume) | PASS: persist-read | ✅ | | riscv64 | virtio-blk (resume, after fix `d2bd448a`) | PASS: persist-read | ✅ | Final accepted runs: amd64 `20260703-183543`, aarch64 `20260703-183710`, riscv64 `20260703-184730`. All six Tripod criteria on all three ISAs: ``` PASS: fleet K PASS: Hermes liveness PASS: Artemis ready PASS: reap K soak PASS: E2E msg flow === TRIPOD DONE === ``` Final prompt on all three: `LithosAnanke v1.5.3` ### riscv64 dict hash divergence — investigated, root-caused, and FIXED Capsule hash identical on all three ISAs: `capsule_hash=0x79c2fefd5231fd24` In the original acceptance run, riscv64's Artemis dict hash diverged (`0x2404108ec607e88f` vs `0xff81e587c380af4a` shared by amd64/aarch64). The divergence was traced to riscv64 taking the blank-disk boot path (`ART-FORMAT` + `ART-SELF-TEST` + `ART-WRITE-TEST`) instead of resume (`ART-RESUME` + `ART-READ-TEST`), because virtio-blk failed to attach — different words execute → different `execution_heat` per `DictEntry` → different hash. The parity harness fingerprinted the path difference correctly. **Why virtio-blk failed on riscv64 (commit `d2bd448a` fixes both):** 1. **MMIO access width.** `VirtioCommonCfg` was declared `__attribute__((packed))`. riscv64 is a strict-alignment target, so GCC compiled packed-struct field access into byte-wise loads/stores. QEMU's virtio common-cfg MMIO handler dispatches on the base offset — a byte read of the 16-bit `queue_size` register (256 = 0x100) returned 0x100 & 0xFF = 0, and the driver bailed with "bad queue size". Every field in the struct is naturally aligned per virtio 1.0, so removing `packed` keeps the layout byte-identical while restoring exact-width access. amd64/aarch64 never saw this because GCC emits normal-width accesses there. 2. **Device selection.** riscv64 boots from a GPT disk that is itself virtio-blk, so two virtio-blk devices sit on bus 0 and the driver takes the first slot — the boot disk, not artemis.img. Fixed deterministically with explicit PCI `addr=` in the Makefile: artdisk at slot 1, boot disk at slot 2. **Result (run 20260703-184730):** riscv64 attaches artemis.img, resumes the LithosAnanke volume, passes persist-read, and its Artemis dict hash is `0xff81e587c380af4a` — identical to amd64 and aarch64. Three-ISA parity closed. ### Latent finding — heartbeat/compile race on riscv64 (REPORTED, NOT FIXED) During the fix campaign, three riscv64 runs (182614, 183257, 183856) failed TRIPOD-TEST with `execute_colon_word: NULL cell in 'K-PUSH' after '(start)'` and `TRIPOD-TEST` landing in the capsule DEFER list at block 2052 load. Bisection cleared the virtio change: run 182614 used pre-fix code with no disk attached and still failed. The discriminator is boot-time heartbeat tick alignment. DoE row 59 (sampled mid capsule-load) reads `...,107,10,33,...` in every passing run and `...,107,9,32,...` in every failing run — one fewer physics count by tick 59. The same binary produced both outcomes on consecutive runs (183856 fail, 184730 pass), so this is host-timing-sensitive, not code-dependent. When the tick lands in the wrong window during Hera's dictionary compilation, compiled words are corrupted (NULL cell in `K-PUSH`) or lost (`TRIPOD-TEST` undefined). This is a pre-existing latent race between the heartbeat physics machinery and dictionary compilation, exposed on riscv64 where boot is slowest under TCG. Logs for all failing runs are committed as evidence. **No fix attempted — awaiting Captain Bob's direction.** --- ## Deferred / Next | Item | Notes | |------|-------| | Logical BAM | Normalize per-block heat so Artemis participates in K-FLEET properly | | ART-TICK heartbeat | Wire into compudynamic tick loop | | Hermes G8 | K-FLEET integration with Hermes thermal mass | | ACL Phase 8 | **STOP before starting** — PKI/Ed25519 thumbdrive (Captain Bob's standing instruction) |