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>
18 KiB
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:
- 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. - 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." - 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.4thblock 4140) has zero call sites anywhere in the tree — no C, no FORTH, no capsule.HERMES-TICK(capsules/hermes/init.4thblock 4116) has zero C call sites. Every caller is Hera poking it manually viaVM-EXEC(capsules/init.4thlines 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-REAPfrees blocks at heat exactly 0; nothing ever brings a block to 0 except an explicitBLK-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-deletedcompudynamics.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:
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.
-
New kernel-only files (
#ifdef __STARKERNEL__), followingcapsule_vm_physics.c's established shape:include/starkernel/artemis_heat_physics.hsrc/starkernel/capsule/artemis_heat_physics.c
State: per-block
{ heat_q48, last_decay_tick }acrossART-DATA-BLKS(22,998 entries), plus theArtemisHeatWindowthe design doc specifies —ArtemisHeatSample { uint32_t live_count; uint32_t reaped_since_last; },ARTEMIS_HEAT_WINDOW_DEPTHproposed 64. -
Lazy decay on access — resolve heat at
BLK-FETCH/BLK-HEAT@with the exactelapsed_tickspattern above, then apply the accumulation term. -
Bounded sweep —
artemis_heat_tick(), called fromvm_tick(), gated to Artemis's VM, processing a fixed batch per tick with a resumable cursor. Mirrorvm_tick_apply_background_decay()directly. This is what allows reap to happen at all without an O(n) stall. -
Adaptive slope —
blk_decay_slope_q48fit 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). -
New FORTH primitives —
BLK-DECAY-SLOPE@, plus a status word mirroringVM-PHYSICS-STATUS. Registration pattern isvoid (*)(VM *vm)+register_word(vm, "NAME", fn); seesrc/word_source/q48_words.c:76,204for the minimal example andmama_forth_words.c:1091for the kernel/MAMA variant (note it registers into both the FORTH and MAMA vocabularies — follow that duplication). -
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 4110–4113 plus 4128 and 4137. Any new blocks belong in 4200–4299 (genuinely unclaimed; 4175–4199 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-ALLOCandBLK-FETCHmintQ.1from nothing,ART-COOLdestroys heat,BLK-FREEzeroes it. 22,998 blocks atQ.1sums to 22,998.0, not 1.0. K-FLEET,K-LOCAL@, andK-CONSERVED?no longer exist — deleted in commit9323f776along withfleet-k.4thandcompudynamics.4th. The BAM acceptance doc's "wireART-K-TOTALintoK-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 MATERIALinARTEMIS.mdand 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
build/tools/mkcapsule --lint capsules→ 26 files, 0 violations.- Hosted build clean, zero warnings under
-Wall -Werror. - Three-arch QEMU acceptance —
make -f Makefile.starkernel ARCH=<arch> clean qemufor amd64, aarch64, riscv64, one at a time, foreground. This is the only acceptance authority for kernel changes. - Behavioural proof that decay is live: allocate blocks, execute
unrelated Artemis words to advance Artemis's own tick count without ever
calling
ART-TICKmanually, then confirm heat has dropped and cold blocks have reaped. This is impossible today — nothing ages — so it is a genuine discriminator. - 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_q48converges to different values rather than sitting at its seed. dict_hashmust stay byte-identical across all three architectures. Established baseline: Hera0x83c2c109100e2ed6, Artemis0x5284ea5cd0f9983c, Hermes0x4159dcb326d79759, Mama0xc88c3c1db6ef601b. 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.