17 KiB
Artemis Block Heat Physics — Design Doc
Date: 2026-07-08 (rev 2026-07-08a)
Branch: lithosananke
Status: Design only. Not implemented. Sibling doc to
HERMES-MESSAGE-CHANNEL-PHYSICS-DESIGN-20260708.md and to
VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md's "Fold-in question" section,
which settled that messages and blocks get their own independent decay
mechanisms — not merged into VMPhysics, not derived from its slope —
each fit from its own rolling window, in separate follow-on docs. This is
that doc for Artemis (block heat). See
HERMES-MESSAGE-CHANNEL-PHYSICS-DESIGN-20260708.md for the message/channel
sibling and HERMES-MESSAGE-BLOCK-STORAGE-DESIGN-20260708.md for the
storage-layer doc this one's precedent comes from (Artemis's allocator
shape).
Author: Captain Bob / Claude Code
Correction (2026-07-09): this doc's original Goal section (rev a)
claimed block heat has no conservation invariant, citing a "settled
fold-in decision" from VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md.
That claim was wrong — it was derived without first reading
.claude/ARTEMIS.md, which is authoritative and states plainly: "Every
block Artemis manages participates in K≡1.0... Artemis's thermal
contribution to the fleet is the sum of her active block heat" and,
more specifically, "Every logical block's heat contributes to Artemis's
K total. Reap must credit K back. Alloc must charge K correctly." A full
Tripod-docs audit (TRIPOD.md/HERMES.md/ARTEMIS.md) confirmed this
directly contradicts what was below. The Goal section is corrected in
place. The rest of this doc's mechanism — an independently-fit decay
rate for blocks, its own ArtemisHeatWindow struct, separate from
word-level, VM-fleet, and Hermes physics — is unaffected: ARTEMIS.md
requires block heat to participate in K≡1.0 as a design target, not
that this doc's narrower decay-rate question also finish the K-FLEET
wiring gap ARTEMIS-BAM-ACCEPTANCE-20260703.md already tracks separately.
Problem
capsules/artemis/init.4th hardcodes 65208 CONSTANT Q-DECAY (Block 4110)
and applies it via straight multiplicative shrink in ART-COOL (Block
4139): for every allocated block in the ART-DATA-BLKS (22,998-entry)
BLK-HEAT array with heat above zero, heat = heat * Q-DECAY.
This is the same "before" picture already diagnosed twice this session:
65208 was independently re-declared as an identical hand-picked constant
in three separate files (compudynamics.4th, hermes/init.4th,
artemis/init.4th) — copy-pasted, uncoordinated, no shared derivation.
VM heat's instance was fixed (capsule_vm_physics.c). Hermes's instance
has a design doc (HERMES-MESSAGE-CHANNEL-PHYSICS-DESIGN-20260708.md),
not yet implemented. This doc is Artemis's.
ARTEMIS-BAM-ACCEPTANCE-20260703.md (Phase 1 acceptance) already flags
the adjacent, larger gap this doc does not attempt to close: ART-K-TOTAL
sums all block heat but was deliberately kept observability only —
not wired into fleet-wide K-FLEET/VM-CONSERVED? — because "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." This doc is scoped to the decay-rate question only (mirroring
Hermes's doc exactly); the Logical BAM gap remains exactly as deferred as
Phase 1 left it — see Explicitly out of scope.
Goal
Replace Q-DECAY with a blk_decay_slope_q48 inferred from observed
block cooling/reap behavior, mirroring the shape both the word-level and
VM-fleet physics engines already use (rolling window, inferred rate,
skip-rather-than-substitute-a-default when unwarmed), applied to a fourth
kind of entity, with its own independent window and its own independent
slope — not shared with Hermes's msg_decay_slope_q48/
ch_decay_slope_q48, not merged into VM-fleet's conservation semantics.
Block heat DOES participate in K≡1.0 — per .claude/ARTEMIS.md's
Compudynamic Invariant: "Every block Artemis manages participates in
K≡1.0. Block fetch, persist, and reap operations must rebalance K
correctly." Blocks are born hot (Q.1, BLK-ALLOC) and decay until
reaped (BLK-HEAT@ 0 = in ART-REAP), same as messages, same as words —
but unlike the earlier (wrong) framing of this doc, that decay is not
conservation-exempt; it's the live thermal contribution ARTEMIS.md
says must sum into Artemis's fleet share. This doc does not itself
design the K crediting mechanism — ARTEMIS.md's Future Material already
specifies the target (Alloc must charge K correctly... Reap must credit K back... The Physical BAM carries no K — only live logical blocks do).
What's still real and still out of scope here: wiring that target
into K-FLEET today is ARTEMIS-BAM-ACCEPTANCE-20260703.md's
already-tracked, already-deferred gap (ART-K-TOTAL sums block heat but
isn't wired into K-FLEET yet) — this doc's job is only the decay
rate, on top of whatever K-crediting mechanism that other gap
eventually completes, not a redesign of it.
Why this can't just copy either existing mechanism verbatim
The reasoning is structurally identical to Hermes's doc — worth stating directly rather than pointing elsewhere, since Artemis's version of it has its own specific shape:
Not word-level's shape: BLK-HEAT entries are keyed by array index
(LBN>IDX, offset into ART-DATA-BLKS), and a freed block's slot is
immediately eligible for BLK-ALLOC to hand back out to an unrelated
future allocation (BLK-FREE zeroes heat and clears the free-map bit in
the same breath — Block 4113). Exactly like a message's arena slot, a
block's identity does not persist across a free/realloc cycle, so
per-entity trajectory replay (word-level's shape) doesn't apply.
Structurally the closest analog is still VM-fleet's rate-recovery
shape, for the same reason as Hermes's doc: the decay law itself
(heat *= Q-DECAY) is fully known — nothing to curve-fit. What's
actually worth inferring is whether the rate is well-matched to
observed block churn (allocation/reap frequency), which is a different
question than either existing engine answers.
One genuine difference from Hermes worth naming: Artemis's ART-TICK
(ART-COOL ART-REAP, Block 4140) is explicitly not wired to the
automatic heartbeat yet — ARTEMIS-BAM-ACCEPTANCE-20260703.md's
Deferred/Next table lists "ART-TICK heartbeat" as its own separate open
item, distinct from the Logical BAM gap. Today, ART-COOL/ART-REAP
only run when something explicitly calls ART-TICK (the self-test words
in Block 4141 do this manually). An inferred blk_decay_slope_q48 is
only as meaningful as the sampling cadence behind it — if ART-TICK
isn't running on a real cadence, there's no real "observed churn" to
infer against. This doc's mechanism therefore has a harder
precondition than Hermes's: the heartbeat-wiring gap needs closing (or
at minimum, a documented decision to sample only on manual ART-TICK
calls, accepting that as the real cadence for now) before rate inference
means anything. Flagged here, not resolved — see Open questions.
The mechanism
1. What "inferring the rate" means here
Same framing as Hermes's doc, applied to blocks: not re-deriving the
known multiplicative law, but tracking observed reap throughput
relative to allocated-block occupancy across ART-TICK calls. High
churn (many BLK-ALLOC/BLK-FREE cycles between ticks) with a
too-slow decay rate lets stale-but-still-hot blocks linger, holding
free-map bits that new allocations need. Low churn with too-aggressive
decay discards heat signal before it's useful for whatever eventually
consumes it (today: nothing does — ART-K-TOTAL is observability only,
per Problem above; this is preparing the mechanism for when something
does).
blk_decay_slope_q48 should be a function of observed reap throughput
relative to ART-DATA-BLKS occupancy at sample time, fit periodically
from a rolling window of samples — the same shape as Hermes's
(live_count, reaped_since_last) sample pair, applied to blocks instead
of messages.
2. ArtemisHeatWindow — new struct, one instance (blocks only)
typedef struct {
uint32_t live_count; /* allocated blocks at this sample */
uint32_t reaped_since_last; /* blocks freed since the prior sample */
} ArtemisHeatSample;
typedef struct {
ArtemisHeatSample samples[ARTEMIS_HEAT_WINDOW_DEPTH];
uint32_t head;
uint32_t count; /* saturates at ARTEMIS_HEAT_WINDOW_DEPTH */
int is_warm; /* count >= ARTEMIS_HEAT_WINDOW_DEPTH */
} ArtemisHeatWindow;
One instance, not two — Artemis has one entity kind (blocks), unlike Hermes's messages/channels split. No structural reason to over-split this the way Hermes's doc deliberately avoided under-splitting.
ARTEMIS_HEAT_WINDOW_DEPTH: proposed 64, not yet settled. Unlike
Hermes's arena (MSG-MAX=32, about to grow), Artemis's scale is already
large and stable — ART-DATA-BLKS=22,998 — so there's no equivalent
"provisional against an about-to-change scale" caveat here. 64 is
proposed only because it matches VM_FLEET_WINDOW_DEPTH and gives a
similarly-sized statistical sample; it is still a starting guess, not
derived from observed churn data, and should be treated the same way
Hermes's window depth is: provisional until a real implementation pass
with instrumented logs exists.
3. Recording — passive, hooked at the existing ART-COOL call site
ART-COOL already sweeps every block with heat above zero once per call
(Block 4139); a new artemis_heat_sample(ArtemisHeatWindow*, uint32_t live_count, uint32_t reaped_since_last) call at the end of ART-COOL
(or at the end of ART-TICK, after ART-REAP has run and
reaped_since_last is known — the more sensible placement, since
ART-COOL alone doesn't know what ART-REAP is about to free) is the
only new call site needed.
This is where the heartbeat-wiring precondition above bites concretely:
if ART-TICK only fires on manual self-test calls, samples only
accumulate on those manual calls too. The window will still function
correctly (it doesn't assume real-time spacing between samples, same as
Hermes's), but "converges to reflect observed churn" only means anything
once ART-TICK runs on a cadence resembling real usage.
4. Inference — heartbeat-gated, same skip-don't-substitute discipline
artemis_heat_tick(), intended to be called from wherever ART-TICK
ends up wired into the real heartbeat (dependent on that gap closing —
see Open questions): when the window is warm, re-fit
blk_decay_slope_q48; when unwarmed, leave the current slope untouched.
Same philosophy as both existing engines and Hermes's doc.
The concrete fit function is left open for the same reason Hermes's doc leaves it open: writing a specific formula without real observed-churn data first would be inventing content, not designing it. A first candidate worth prototyping, matching Hermes's doc's proposed starting hypothesis for consistency: target reap-latency-in-ticks, adjust slope proportionally to observed-vs-target ratio.
5. Application — replaces Q-DECAY Q.* at the one cool-sweep site
ART-COOL's Q-DECAY Q.* becomes blk_decay_slope_q48 Q.*. No change
to the multiplicative decay shape itself, only to where the rate comes
from. ART-REAP and BLK-ALLOC/BLK-FREE/BLK-FETCH are untouched —
same "don't modify the hard-locked allocator primitives" discipline
HERMES-MESSAGE-BLOCK-STORAGE-DESIGN-20260708.md states explicitly for
FM-*/BLK-ALLOC/BLK-FREE (Blocks 4110–4113, ★ HARD LOCKED per
capsules/MANIFEST.md) — this doc only touches ART-COOL (Block 4139),
which is not in that locked set.
Consumer code migration
artemis/init.4thBlock 4110:65208 CONSTANT Q-DECAYdeleted.artemis/init.4thBlock 4139 (ART-COOL):Q-DECAYreference replaced with a call to fetch the currentblk_decay_slope_q48(new C primitive, e.g.BLK-DECAY-SLOPE@); gains the sample-recording call described in section 3 (placement — insideART-COOLorART-TICK— left as an implementation decision, see section 3).- New kernel-only file pair, matching
capsule_vm_physics.c's and Hermes's plannedhermes_heat_physics.c's scope (Artemis-specific state, no hosted-build equivalent):src/starkernel/capsule/artemis_heat_physics.cinclude/starkernel/artemis_heat_physics.h
- New FORTH-visible primitive:
BLK-DECAY-SLOPE@, plus a status word mirroringVM-PHYSICS-STATUS's and Hermes's planned status word's shape for diagnostics.
What gets deleted
artemis/init.4thBlock 4110:65208 CONSTANT Q-DECAY.
Nothing else — ART-COOL/ART-REAP/ART-TICK/BLK-ALLOC/BLK-FREE/
BLK-FETCH keep their existing structure, only the decay-rate source at
the one ART-COOL call site changes.
Verification approach
- Three-arch acceptance as usual (kernel-only code,
#ifdef __STARKERNEL__gates per.claude/CLAUDE.md). - A targeted test: drive real block churn (repeated
BLK-ALLOC/BLK-FREEcycles viaART-SELF-TEST-style traffic, Block 4141) at two different rates across separate boot runs, confirmblk_decay_slope_q48converges to different values — the same class of test Hermes's doc and VM-fleet's design both use to confirm the inferred rate actually responds to real observed activity rather than sitting at its seed value. - Confirm
ArtemisHeatWindow's dead-entity handling (a block freed mid-window, its heat zeroed byBLK-FREEbefore the next sample) doesn't fault or corrupt the sample count — same class of test as VM-fleet's "kill-during-warm-up" test and Hermes's planned equivalent. - Specific to this doc's precondition: before trusting any convergence
result, confirm
ART-TICKis actually firing at a real, known cadence during the test run (manual self-test calls, or the heartbeat wiring if that gap is closed first) — a window fed by an unpredictable or absent cadence will produce numbers that look like convergence but aren't meaningful.
Explicitly out of scope
- Logical BAM /
K-FLEETwiring. Already deferred inARTEMIS-BAM-ACCEPTANCE-20260703.md's own Phase 1 acceptance: "ART-K-TOTALsums all block heats but is not wired intoK-FLEET... The correct fix (Logical BAM) requires normalizing per-block heat... That is deferred." This doc gives block heat a decay rate, not the K-crediting wiring itself — those are separate problems. PerARTEMIS.md, block heat IS meant to be conservation-eligible (see Goal, corrected); finishing that wiring remains Artemis's own gap to close on its own schedule, independently of Hermes's parallel storage work, and is not part of this decay-rate design. ART-TICKheartbeat wiring. Named as a precondition above, but closing it is a separate, already-identified deferred item (ARTEMIS-BAM-ACCEPTANCE-20260703.md's Deferred/Next table), not part of this design.- Hermes message/channel heat — sibling doc,
HERMES-MESSAGE-CHANNEL-PHYSICS-DESIGN-20260708.md. No shared state, window, or slope between the two; each VM's heat physics is entirely its own, matching the zero-cross-VM-coupling architecture confirmed inHERMES-MESSAGE-BLOCK-STORAGE-DESIGN-20260708.md. - The riscv64 heartbeat/compile race noted in
ARTEMIS-BAM-ACCEPTANCE-20260703.mdas an unfixed, reported finding ("awaiting Captain Bob's direction"). Unrelated to block heat decay; not addressed here. - The multi-level DoE rewrite (word/VM-fleet/message/block as four independent metric spaces) — this doc gives block heat its decay mechanism; observing it as part of a real experiment is separate, larger work already on the punch list.
- Implementation — this is a design doc. Writing
artemis_heat_physics.cis follow-on work, not part of this pass.
Open questions (explicitly not resolved — flagged, not guessed at)
- The
ART-TICKheartbeat-wiring precondition. This doc's mechanism only produces meaningful results onceART-TICKruns on a real cadence, not just manual self-test calls. Whether to close that gap first, or accept manual-call-driven sampling as the real cadence for an initial implementation pass, is unresolved. ARTEMIS_HEAT_WINDOW_DEPTHvalue. Proposed 64, not derived from real observed churn data — needs a real implementation pass with instrumented boot logs before treating this as settled.- The concrete fit function for turning
(live_count, reaped_since_last)samples intoblk_decay_slope_q48. Section 4 deliberately stops short of a formula, same as Hermes's doc. reaped_since_lastbookkeeping mechanism —ART-REAPisn't currently instrumented to report a freed-count to whatever records the sample; same open plumbing question Hermes's doc flags forMSG-REAP.- Sample placement — inside
ART-COOL(beforeART-REAPruns, soreaped_since_lastreflects the previous tick's reaps) vs. insideART-TICKafter bothART-COOLandART-REAPhave run (so the sample reflects this tick's reaps exactly). Section 3 leans toward the latter but doesn't commit.