Add Artemis compudynamics implementation plan; add stress campaign fixture

The 30-replicate Artemis surface-stress campaign ran across all three
architectures: 30 reps x 50 trials x 3 arches = 4500 trials, zero failures.
The block_words.c stale-pointer cache-aliasing fix holds at scale.

Adds ART-STRESS-CAMPAIGN (block 4174) plus ART-STRESS-REP rep-tagging in
the CSV header/summary emitters, so a multi-replicate run is distinguishable
in the serial log. Campaign auto-invoke is left enabled in block 4170 for
now; Makefile.starkernel's QEMU boot deadlines stay at 12h to accommodate
long-running experiments.

Adds docs/working/architecture/ARTEMIS-COMPUDYNAMICS-IMPLEMENTATION-PLAN-20260802.md,
which documents the real gap this campaign exposed: block heat and message
heat do not decay at all. ART-TICK has zero call sites anywhere in the tree,
and HERMES-TICK has zero C call sites -- every caller is Hera poking it by
hand. BLK-HEAT@/MSG-HEAT@ read a number nothing ages, so blocks never reap
by cooling and message TTL never expires on its own.

The plan mirrors word-level physics as the reference model: lazy decay at
each access point against vm->heartbeat.tick_count, plus a bounded
background sweep with a resumable cursor (the existing answer to "sweeping
22,998 blocks per tick is too expensive"). Phase 1 Artemis, Phase 2 Hermes,
Phase 3 K participation deferred behind the Logical BAM.

The plan's preamble also records a wrong turn taken while investigating:
chasing VM-fleet heat instead of block heat, and building synthetic
Hera-driven VM-EXEC calls to force a physics reading -- which TRIPOD.md
prohibits outright. That work was reverted; the record is kept so it isn't
repeated.

Status: plan approved in shape, NOT final and NOT started. Six open
questions need answers and further design discussion is pending.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-02 21:49:16 -04:00
co-authored by Claude Opus 5
parent 1cb68502fb
commit 7597a9ccd4
11 changed files with 210228 additions and 5264 deletions
@@ -0,0 +1,376 @@
# Artemis (and Hermes) Compudynamics — Implementation Plan
**Date:** 2026-08-02
**Branch:** `master`
**Status:** Plan approved in shape, **not final and not started.** Captain Bob
has more to discuss on the design before implementation begins — this document
is expected to change as a result. Do not treat it as settled, and do not
start coding from it as written. The six Open Questions are the known gaps;
the pending discussion may add or reframe others.
**Author:** Captain Bob / Claude Code
**Siblings:** `ARTEMIS-BLOCK-PHYSICS-DESIGN-20260708.md` (the decay-rate design
this implements), `HERMES-MESSAGE-CHANNEL-PHYSICS-DESIGN-20260708.md`,
`ARTEMIS-BAM-ACCEPTANCE-20260703.md` (which tracks the `ART-TICK heartbeat`
gap this closes), `VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md` (pattern source).
---
## Preamble: how this plan came to be written
This plan exists because of a wasted day, and the failure is worth recording
so it isn't repeated.
On 2026-08-02 a 30-replicate Artemis surface-stress campaign was run across
all three architectures to confirm the `block_words.c` stale-pointer
cache-aliasing fix held at scale. It did: **4500 trials, zero failures,
amd64/aarch64/riscv64**. That result is real and stands.
While reviewing the campaign's heartbeat CSV, the assistant observed the
`hera_heat_q48` / `hermes_heat_q48` / `artemis_heat_q48` columns sitting
perfectly flat (`65536, 0, 0`) across all 51,092 ticks, concluded this was a
defect, and rebuilt the campaign driver to route every stress-test phase
through Hera via `VM-EXEC` so that fleet heat would move.
That was wrong three separate ways:
1. **Wrong metric.** Those columns are *VM-fleet* heat
(`capsule_vm_physics.c`, `vm_physics_heat_of()`). The quantity of interest
for Artemis is *block* heat (`BLK-HEAT` / `ART-K-TOTAL`). Different
mechanisms, no connection between them.
2. **Wrong method.** Manufacturing cross-VM calls to force a physics reading
is explicitly prohibited: `TRIPOD.md` — *"No VM manages another VM's
internal heartbeat"*, *"There is no 'send to the hottest VM.' That model is
wrong and must not be implemented"*; `capsule_vm_physics.h:35-37` — *"a
passive observer of VM activity, never a driver of it."*
3. **Wrong premise.** Block heat cannot move today under any workload,
because nothing ages it (see below). No amount of driving would have
produced a signal.
All three were avoidable by reading `.claude/TRIPOD.md` and `.claude/ARTEMIS.md`
completely before touching Tripod code — which both files instruct in their own
headers, and which `.claude/CLAUDE.md` also instructs. They were skimmed by
keyword instead. Keyword search cannot surface a prohibition, because
prohibitions do not contain the term you are searching for.
The capsule changes from that detour have been reverted. **Standing rule
going forward: read the governing documents in full before starting any major
piece of work in this repo.**
---
## The actual defect
Block heat and message heat do not decay. Not "decay incorrectly" — **do not
decay at all**, unless something manually calls a sweep word.
- `ART-TICK` (`ART-COOL ART-REAP`, `capsules/artemis/init.4th` block 4140) has
**zero call sites** anywhere in the tree — no C, no FORTH, no capsule.
- `HERMES-TICK` (`capsules/hermes/init.4th` block 4116) has **zero C call
sites**. Every caller is Hera poking it manually via `VM-EXEC`
(`capsules/init.4th` lines 31, 47, 66, 83, 130).
`BLK-HEAT@` and `MSG-HEAT@` read a stored number that nothing ages. Heat is
frozen between manual pokes — neither lazy nor scheduled, simply absent.
Consequences:
- Blocks never reap by cooling. `ART-REAP` frees blocks at heat exactly 0;
nothing ever brings a block to 0 except an explicit `BLK-FREE`.
- Message TTL never expires on its own, contradicting `HERMES.md`'s stated
model (*"A message that nobody answers simply cools to death"*).
- `Q-DECAY` (65208) is hardcoded and hand-copied into three separate capsules
(`artemis/init.4th`, `hermes/init.4th`, and the now-deleted
`compudynamics.4th`) with no shared derivation — flagged in the block
physics design doc as the "before" picture.
`ARTEMIS-BAM-ACCEPTANCE-20260703.md` already tracks this as a named deferred
item ("ART-TICK heartbeat — wire into compudynamic tick loop"). This plan
closes it.
---
## The governing model: mirror how words work
Per Captain Bob's direction: **word-level execution physics is the reference
implementation.** Messages decay on **TTL**; blocks decay on **access
frequency**. Two distinct behaviours, one shared mechanism shape.
The word model, verified in source, has four parts:
**1. Storage.** `DictPhysics` (`include/vm.h:257-268`) carries
`last_decay_tick`, commented in-source as *"the only clock Loop #3 decay
reads."* The two `*_ns` fields beside it are explicitly marked **diagnostics
only, never read for decay** — the residue of the wall-clock bug fixed in
`VM-FLEET-ATTRACTOR-DESIGN-20260705.md` rev t.
**2. Lazy decay at every access point.** `physics_pre_execute()`
(`src/physics_execution_hooks.c:85-89`), `physics_on_lookup()` (`:203-215`),
and the kernel's inline duplicates (`src/starkernel/vm/vm_core.c:676, 870,
880`) all do exactly this:
```c
uint64_t elapsed_ticks = vm->heartbeat.tick_count - word->physics.last_decay_tick;
physics_metadata_apply_linear_decay(word, elapsed_ticks, vm);
word->physics.last_decay_tick = vm->heartbeat.tick_count;
/* then accumulate: */
physics_execution_heat_increment(word);
```
**3. Bounded background sweep with a resumable cursor.**
`vm_tick_apply_background_decay()` (`src/starkernel/vm/vm_runtime.c:357-388`)
processes at most `HEARTBEAT_DECAY_BATCH` dictionary entries per tick and
stores `vm->heartbeat_decay_cursor_id` to resume on the next tick.
**This is the answer to "sweeping 22,998 blocks per tick is too expensive."**
An earlier fleet-DoE attempt stalled outright on exactly that cost. The word
engine never sweeps its whole space in one tick — it amortizes. Artemis must
do the same.
**4. Decay math.** `physics_metadata_apply_linear_decay()`
(`src/physics_metadata.c:320-386`): `decay = (elapsed_ticks * slope_q48) >> 16`,
subtractive, clamped at zero. The slope is adaptive (`vm->decay_slope_q48`,
tuned by Loop #6), and the whole path is gated by
`ssm_config->L3_linear_decay` so L8 can switch it off.
### Two model differences to resolve, not assume
| | Words today | Artemis today | Consequence |
|---|---|---|---|
| Decay shape | **linear** subtract | **multiplicative** (`heat * 65208/65536`) | Multiplicative approaches zero asymptotically but may never reach exactly 0 in Q48.16; `ART-REAP` fires only at exactly 0. |
| On access | **increments** heat | `BLK-FETCH` **resets** to `Q.1` | Reset is recency-only; increment is frequency-sensitive. Bob named *frequency* as the block signal. |
Recommendation: match the word model on both counts. Confirm first (O2, O4) —
this changes when blocks reap.
---
## Design decisions
### D1. Block heat becomes C-side state
Today `BLK-HEAT` is a FORTH `CREATE ... ALLOT` array (block 4137, marked
immutable in `MANIFEST.md`). Word heat by contrast lives in C
(`DictEntry.execution_heat` + `DictPhysics`), with FORTH words reading it.
Mirror that. Block heat plus `last_decay_tick` move into
`src/starkernel/capsule/artemis_heat_physics.c`, with FORTH primitives for the
observational surface.
This is the decision that makes everything else fall out: the background sweep
becomes plain C called from `vm_tick()`, and **no C→FORTH bridge is required.**
That matters more than it might appear. **A mechanism for C to invoke a named
FORTH word on a tick does not exist in this codebase** — there is no
`vm_execute_word_by_name`, and `capsule_vm_hooks.c` carries only birth-time
hooks (exec, dict-hash, vm-alloc). Building one would be a substantial new
mechanism with its own risks. This design avoids needing it.
Per the ruling recorded in `VM-FLEET-ATTRACTOR-DESIGN-20260705.md` rev s —
*"TRIPOD.md's/ARTEMIS.md's 'StarForth dialect ONLY' applies to a thin
administrative/observational word surface; the underlying mechanics are meant
to be C99"* — this is in bounds. **Confirm the ruling still holds (O5);** D1
depends entirely on it.
### D2. Each VM's own heartbeat tick is the clock
`HeartbeatState` is embedded per-VM (`include/vm.h:280-298`). `tick_count`
advances only from `vm_tick()`, driven by that VM's own word executions
(`src/physics_execution_hooks.c:172-177`). Artemis's blocks therefore age on
Artemis's own tick. No cross-VM coupling, nothing injected, nothing to drive.
One clock, as the standing rule requires.
**Known consequence:** an idle VM does not tick, so its blocks and messages do
not age. For words this is explicitly intentional and documented as such. For
**messages it is a genuine problem** — a TTL that stops while Hermes is idle
is not a TTL. See O3.
### D3. The insertion point already exists
`vm_tick()` (`src/starkernel/vm/vm_runtime.c:145-149`) carries two
commented-out "future plugin" slots. That is the natural home for
`artemis_heat_tick()` and later `hermes_heat_tick()`.
---
## Phase 1 — Artemis block heat
**Goal:** block heat ages against Artemis's heartbeat, accumulates on access,
and reaps at zero, with an inferred slope replacing the hardcoded constant.
1. **New kernel-only files** (`#ifdef __STARKERNEL__`), following
`capsule_vm_physics.c`'s established shape:
- `include/starkernel/artemis_heat_physics.h`
- `src/starkernel/capsule/artemis_heat_physics.c`
State: per-block `{ heat_q48, last_decay_tick }` across `ART-DATA-BLKS`
(22,998 entries), plus the `ArtemisHeatWindow` the design doc specifies —
`ArtemisHeatSample { uint32_t live_count; uint32_t reaped_since_last; }`,
`ARTEMIS_HEAT_WINDOW_DEPTH` proposed 64.
2. **Lazy decay on access** — resolve heat at `BLK-FETCH` / `BLK-HEAT@` with
the exact `elapsed_ticks` pattern above, then apply the accumulation term.
3. **Bounded sweep**`artemis_heat_tick()`, called from `vm_tick()`, gated
to Artemis's VM, processing a fixed batch per tick with a resumable cursor.
Mirror `vm_tick_apply_background_decay()` directly. This is what allows
reap to happen at all without an O(n) stall.
4. **Adaptive slope**`blk_decay_slope_q48` fit from the window; when the
window is unwarmed, **skip the update rather than substituting a default**
(the discipline both existing engines use, and the specific trap that
froze VM-fleet physics at zero twice — see rev l and rev m of the fleet
doc).
5. **New FORTH primitives**`BLK-DECAY-SLOPE@`, plus a status word
mirroring `VM-PHYSICS-STATUS`. Registration pattern is
`void (*)(VM *vm)` + `register_word(vm, "NAME", fn)`; see
`src/word_source/q48_words.c:76,204` for the minimal example and
`mama_forth_words.c:1091` for the kernel/MAMA variant (note it registers
into both the FORTH and MAMA vocabularies — follow that duplication).
6. **Capsule changes**`ART-COOL` (block 4139) stops being a full sweep;
`ART-TICK` (block 4140) is retained for manual diagnostics. Both blocks are
editable — the hard-locked set is 41104113 plus 4128 and 4137. Any new
blocks belong in **42004299** (genuinely unclaimed; 41754199 is nominally
Hermes extension space).
### Available Q48.16 vocabulary
`Q.+ Q.- Q.* Q./ Q.ABS Q.NEG Q.LOG Q.EXP Q.SQRT Q.FROM-INT Q.TO-INT Q.1 Q.0
Q.SCALE Q.= Q.< Q.> Q.0= Q.MAX Q.MIN Q.PRINT` — all registered from
`src/word_source/q48_words.c:200-223`. There is **no** `Q.MOD` or remainder
word; if decay must be exactly conservative, that arithmetic has to come from
C. (Note `src/word_source/q48_16_words.c` exists but has no register function
and is not referenced by `word_registry.c` — dead code, do not follow it.)
---
## Phase 2 — Hermes message and channel heat
Same mechanism, after Artemis is proven. Per
`HERMES-MESSAGE-CHANNEL-PHYSICS-DESIGN-20260708.md`: `hermes_heat_physics.c`,
primitives `MSG-DECAY-SLOPE@` / `CH-DECAY-SLOPE@`, sampling hooked at
`MSG-COOL-ALL` (block 4108) and `CH-COOL-ALL` (block 4114).
**Behavioural difference from blocks:** messages are pure TTL — born hot,
decay only, never refreshed on access. No accumulation term. That is the whole
distinction Bob drew between the two.
**Correct that design doc before implementing it.** It proposes calling
`hermes_heat_tick()` from *"`HERMES-TICK`'s existing heartbeat path"* and
contrasts this against Artemis as having the harder problem for lacking one.
**That premise is false** — verified 2026-08-02: `HERMES-TICK` has no
heartbeat path either. Both VMs are in identical positions, and the doc's
sizing of the two efforts is wrong as written.
Message nodes are 8 cells (`HERMES.md:305-313`) with heat at cell 5. A
`last_decay_tick` needs either a 9th cell or C-side storage — C-side, per D1.
---
## Phase 3 — K participation (deferred; needs design, not wiring)
`.claude/ARTEMIS.md` requires that blocks participate in K≡1.0: *"Every
logical block's heat contributes to Artemis's K total. Reap must credit K
back. Alloc must charge K correctly."* Today nothing does, and closing it is
not a small job:
- **The current model is not conservative by construction.** `BLK-ALLOC` and
`BLK-FETCH` mint `Q.1` from nothing, `ART-COOL` destroys heat, `BLK-FREE`
zeroes it. 22,998 blocks at `Q.1` sums to 22,998.0, not 1.0.
- **`K-FLEET`, `K-LOCAL@`, and `K-CONSERVED?` no longer exist** — deleted in
commit `9323f776` along with `fleet-k.4th` and `compudynamics.4th`. The BAM
acceptance doc's "wire `ART-K-TOTAL` into `K-FLEET`" item refers to words
that are gone. Fleet conservation today is C-side only
(`vm_physics_conserved()`, 5% tolerance, `capsule_vm_physics.c:456`).
- Proper normalization is the **Logical BAM**, which is `FUTURE MATERIAL` in
`ARTEMIS.md` and gated behind Captain Bob explicitly reopening it.
Contrast with VM-fleet physics, which *is* conservative by construction via a
single balanced primitive `vm_physics_transfer(from, to, amount)` — the sum
invariant holds by induction rather than by enforcement. Blocks have no
equivalent primitive. Designing one is real work.
**Stale text to fix:** `VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md`'s
"Fold-in question" section states that conservation does *not* generalize to
messages or blocks. The 2026-07-09 corrections on both sibling docs overturn
this, citing `.claude/ARTEMIS.md` and `.claude/HERMES.md` as authoritative.
The corrections win; the stale passage should be amended so it stops
contradicting them.
---
## Open questions — answer before writing code
**O1.** `Q-DECAY` lives in block **4110, marked ★ HARD LOCKED** in
`MANIFEST.md`. The block physics design doc says delete it. The manifest says
don't touch that block. Which wins — delete, or leave it and shadow it?
**O2.** Linear-subtractive or multiplicative decay for blocks? The word model
is linear; Artemis is currently multiplicative. This determines whether reap
ever fires.
**O3.** Idle-VM TTL. If Hermes executes no words it accrues no ticks, so
messages never expire. Accept this (TTL means "ticks of that VM's own
execution"), or does message aging need a different clock source? Note that
any answer involving a second clock conflicts with the one-clock rule.
**O4.** `BLK-FETCH` currently *resets* heat to `Q.1`. Change to increment
(frequency-sensitive, matches words, matches "how frequently" as the stated
signal) or keep reset (recency-only)?
**O5.** Confirm the C99-mechanics / FORTH-surface split still holds. D1 —
and therefore the whole no-bridge-needed argument — depends on it.
**O6.** `ARTEMIS_HEAT_WINDOW_DEPTH` (proposed 64) and the concrete slope-fit
function are both explicitly unresolved in the design docs. Start with the
proposed values and instrument, or settle them up front?
---
## Known risk
`ARTEMIS-BAM-ACCEPTANCE-20260703.md` records an **unfixed riscv64
heartbeat/compile race**: identical binaries alternately pass and fail
`TRIPOD-TEST` with `execute_colon_word: NULL cell in 'K-PUSH' after
'(start)'`, discriminated by boot-time heartbeat tick alignment (DoE row 59
reading `...,107,10,33,...` passing versus `...,107,9,32,...` failing).
This plan adds work inside `vm_tick()`, which shifts tick alignment. **Expect
to encounter this race.** It was reported and never fixed — "awaiting Captain
Bob's direction."
---
## Verification
1. `build/tools/mkcapsule --lint capsules` → 26 files, 0 violations.
2. Hosted build clean, zero warnings under `-Wall -Werror`.
3. Three-arch QEMU acceptance — `make -f Makefile.starkernel ARCH=<arch> clean
qemu` for amd64, aarch64, riscv64, one at a time, foreground. This is the
only acceptance authority for kernel changes.
4. **Behavioural proof that decay is live:** allocate blocks, execute
unrelated Artemis words to advance Artemis's own tick count *without ever
calling `ART-TICK` manually*, then confirm heat has dropped and cold blocks
have reaped. This is impossible today — nothing ages — so it is a genuine
discriminator.
5. **Slope responds to real churn** (the design doc's own test): drive block
churn at two different rates across separate boots and confirm
`blk_decay_slope_q48` converges to different values rather than sitting at
its seed.
6. **`dict_hash` must stay byte-identical across all three architectures.**
Established baseline: Hera `0x83c2c109100e2ed6`, Artemis
`0x5284ea5cd0f9983c`, Hermes `0x4159dcb326d79759`, Mama
`0xc88c3c1db6ef601b`. Any divergence means a wall-clock dependency has
crept in — precisely the bug class rev t of the fleet doc fixed.
## Not in scope
- Logical BAM / Physical BAM split, thermal zones (Phase 3 blocker,
`FUTURE MATERIAL`, requires explicit reopening)
- ACL Phase 8 / PKI — standing instruction is **STOP before starting**
- Any commit without Captain Bob asking for one
---
*Governing documents for this work, to be read completely before starting:*
`.claude/ARTEMIS.md`, `.claude/TRIPOD.md`, `.claude/HERMES.md`,
`.claude/CLAUDE.md`, and the four sibling design docs named in the header.