Files
LithosAnanake/docs/working/architecture/ARTEMIS-BLOCK-PHYSICS-DESIGN-20260708.md
T

321 lines
17 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 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)
```c
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 41104113, ★ 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.4th` Block 4110**: `65208 CONSTANT Q-DECAY` deleted.
- **`artemis/init.4th` Block 4139** (`ART-COOL`): `Q-DECAY` reference
replaced with a call to fetch the current `blk_decay_slope_q48` (new C
primitive, e.g. `BLK-DECAY-SLOPE@`); gains the sample-recording call
described in section 3 (placement — inside `ART-COOL` or `ART-TICK`
left as an implementation decision, see section 3).
- New kernel-only file pair, matching `capsule_vm_physics.c`'s and
Hermes's planned `hermes_heat_physics.c`'s scope (Artemis-specific
state, no hosted-build equivalent):
- `src/starkernel/capsule/artemis_heat_physics.c`
- `include/starkernel/artemis_heat_physics.h`
- New FORTH-visible primitive: `BLK-DECAY-SLOPE@`, plus a status word
mirroring `VM-PHYSICS-STATUS`'s and Hermes's planned status word's
shape for diagnostics.
## What gets deleted
- `artemis/init.4th` Block 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-FREE` cycles via `ART-SELF-TEST`-style traffic, Block 4141) at two
different rates across separate boot runs, confirm `blk_decay_slope_q48`
converges 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 by `BLK-FREE` before 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-TICK` is 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-FLEET` wiring.** Already deferred in
`ARTEMIS-BAM-ACCEPTANCE-20260703.md`'s own Phase 1 acceptance:
"`ART-K-TOTAL` sums all block heats but is not wired into `K-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. Per `ARTEMIS.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-TICK` heartbeat 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 in
`HERMES-MESSAGE-BLOCK-STORAGE-DESIGN-20260708.md`.
- **The riscv64 heartbeat/compile race** noted in
`ARTEMIS-BAM-ACCEPTANCE-20260703.md` as 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.c` is follow-on work, not part of this pass.
## Open questions (explicitly not resolved — flagged, not guessed at)
1. **The `ART-TICK` heartbeat-wiring precondition.** This doc's mechanism
only produces meaningful results once `ART-TICK` runs 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.
2. **`ARTEMIS_HEAT_WINDOW_DEPTH` value.** Proposed 64, not derived from
real observed churn data — needs a real implementation pass with
instrumented boot logs before treating this as settled.
3. **The concrete fit function** for turning `(live_count,
reaped_since_last)` samples into `blk_decay_slope_q48`. Section 4
deliberately stops short of a formula, same as Hermes's doc.
4. **`reaped_since_last` bookkeeping mechanism** — `ART-REAP` isn't
currently instrumented to report a freed-count to whatever records the
sample; same open plumbing question Hermes's doc flags for
`MSG-REAP`.
5. **Sample placement** — inside `ART-COOL` (before `ART-REAP` runs, so
`reaped_since_last` reflects the *previous* tick's reaps) vs. inside
`ART-TICK` after both `ART-COOL` and `ART-REAP` have run (so the
sample reflects this tick's reaps exactly). Section 3 leans toward the
latter but doesn't commit.