Files
LithosAnanake/docs/working/architecture/ARTEMIS-BAM-ACCEPTANCE-20260703.md
T

235 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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) |