17 KiB
Work Log: July 5, 2026
K-PUSH Dictionary Shadowing Fix → Dynamic VM Fleet Physics Design & Implementation
Branch: lithosananke
Last commit this session: 7a1383a0 — "docs: note future BIRTH-permission question in out-of-scope section"
Tree state at session end: clean, fully pushed to origin/lithosananke
Why this log exists
This session ran long enough to need archiving. It covers two large, sequential efforts: (1) root-causing and fixing a real VM crash bug, and (2) using that bug as the trigger to design and implement a full replacement for the hardcoded 3-VM Tripod fleet's heat/conservation mechanism. Read this before resuming work — it's the fast path back to full context without re-deriving everything from the git log.
Start here for the live design doc:
docs/working/architecture/VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md (rev g
as of session end). That doc is the authoritative, up-to-date record of the
design — this worklog is a narrative companion, not a replacement.
Also authoritative for this session's bug:
docs/working/architecture/K-PUSH-DICTIONARY-SHADOWING-BUG-20260704.md — the
full bug report (root cause, fix, verification), written and committed before
this session's design work began.
Part 1: K-PUSH dictionary shadowing bug (carried in from prior session, closed out here)
Symptom: K-PUSH (in the old fleet-k.4th) crashed with a NULL-cell
fault under certain heat-reorg timing.
Root cause: A kernel C primitive and a FORTH capsule constant were both
named VM-COUNT. Dictionary lookup picked whichever one happened to be
"newest" by bucket position — and the heat-reorg pass (dict_reorganize_buckets_by_heat,
a wall-clock-gated qsort by execution_heat) could reshuffle bucket order
in a way that broke the assumption that "newest" meant "most recently
defined." This made word resolution non-deterministic under specific timing,
and K-PUSH (which needed the FORTH constant, not the C primitive) sometimes
got the wrong one.
Fix: vm_dict_resolve_in_bucket() (src/dictionary_management.c) — a
full-bucket scan that walks vm->latest's link chain to determine true
definition age, independent of physical bucket/array position. Applied to
every lookup path: vm_find_word, the hotwords cache's Stage-2 fallback,
and dict_find_word_heat_aware. Cache coherence fixes (eviction on
hide/smudge/redefine) went in alongside it.
Verification: Three-arch QEMU acceptance with matching dict_hash
across amd64/aarch64/riscv64 — the first time in the project's history this
had been achieved. This was the acceptance criterion that closed the bug out.
A false start along the way (reverted cleanly): An initial hypothesis
blamed a mid-compile forward-reference race in the capsule loader, and a
speculative "atomic colon-definition rollback" fix was built for it
(a8ea30e7, marked WIP). It turned out not to be the real bug — it never
engaged for the actually-failing block, and it caused a real regression
(Hermes CH-SCAN infinite loop, observed at ~2.9 trillion ticks before being
killed). Once the real fix (age-honoring resolution) made it unnecessary, it
was reverted via git checkout a8ea30e7^ -- src/starkernel/capsule/capsule_loader.c
(commit 6816063f), restoring the original proven per-line defer mechanism.
Captain Bob explicitly approved chasing the harder, correct C-level fix over
a quick capsule-loader patch: "I say fix the C99 part is the correct
solution. I know it's the harder one too."
Full report: docs/working/architecture/K-PUSH-DICTIONARY-SHADOWING-BUG-20260704.md
(commit 89024043).
Part 2: From bug fix to architectural question
Fixing the shadowing bug exposed a deeper issue Captain Bob flagged directly:
Hera's fleet is hardcoded to exactly 3 VMs (VM-COUNT), in two ways —
fixed-size arrays (CREATE ... VM-COUNT ... ALLOT, no realloc in this FORTH)
and hand-unrolled loops (K-REBALANCE, K-STATUS, VM-HOTTEST, CD-TICK
all spelled out exactly three VM indices).
Captain Bob's framing for the fix, given verbatim because it set the direction for everything that followed:
"I'm actually trying to eliminate any notion of special cases. Once we have the capsule registry written, we're going to wire in the same machinery we use for word execution to decide VM execution turn... we do NOT want a scheduler. The execution depends on the VM state."
"there is a slope calculation with words that is a 0-1 Q48.16 calculation for decay and a feedback loop reshaping the slope every heartbeat. same concept for vm's" — pointing directly at Loop #6 (
inference_engine.c,infer_decay_slope_q48) as the template to mirror, not reuse.
This produced the core design commitment: VM-level heat governance becomes a
structural parallel to the existing word-level physics engine (heat
accrual, decay, rolling-window history, regression-inferred adaptive slope)
— not a shared implementation, and explicitly not a scheduler. Physics
stays a passive observer of real VM-EXEC/VM-CALL/VM-STEP dispatch; it
never decides whose turn it is to run.
Part 3: The design doc — iterative gap-closing (revs a→g)
docs/working/architecture/VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.md went
through 7 revisions this session, each committed separately:
- rev a (
119bfc83) — initial doc. - rev b (
5ba733db) — Captain Bob posted concrete struct definitions, weighting formulas, concurrency notes; two explicit open items. - rev c (
ee87ec0e) — closed all four implementation gaps found during an honest self-audit (Captain Bob asked directly: "do you have enough information to build this over several breaks in the session?" — the audit found gaps rather than assuming yes). - rev d (
c85c0f95) — corrected an incomplete consumer-migration audit (first pass missedcapsules/hermes/init.4thandcapsules/artemis/init.4thentirely — Hermes turned out to be a real, previously-unaudited consumer of the doomed hooks) and posed the "fold-in question" (does Hermes/Artemis's own heat economy merge into VM-fleet heat, or stay separate?). - rev e (
cacac77a) — settled the fold-in question: no merge, sibling instances. Each level (words, VMs, messages, blocks) keeps its own independent heat quantity; only the mechanism (passive accrual, lazy decay, rolling window, regression-inferred slope) is meant to generalize. Evidence:Q-DECAY = 65208was independently hand-copied into three separate files (compudynamics.4th,hermes/init.4th,artemis/init.4th) with zero coordination — the literal "before" picture words already moved past via Loop #6. - rev f (
be611cba) — execution rules added at Captain Bob's explicit instruction before implementation began: small units, clean+make at checkpoints, QEMU when a unit affects boot behavior, one ISA at a time during iteration (full three-arch reserved for milestones). - rev g (
c8af58c7) — kill's heat-return mechanism replaced mid-implementation (see Part 5) and the doc updated to match what was actually built.
Part 4: Implementation — small units, each checkpointed
Executed strictly per the rev-f rules: build clean, checkpoint, QEMU when the unit touched boot behavior, one ISA (amd64) during iteration.
-
cc9a4da2— New kernel-only file pair,capsule_vm_physics.c/.h.VMPhysics(execution_heat_q48/last_active_ns/is_live) andVMFleetWindow(64-deep circular touch-history buffer) as file-scope state, mirroringcapsule_birth.c's ownvm_registry_headpattern rather than a publicVMRegistryEntryfield.vm_physics_transfer(subtract-then-add, clamped at 0) is the one conservative primitive everything else composes from. The fleet-wide inference reimplementsinfer_decay_slope_q48's closed-form log-linear OLS regression against the fleet's own touch-history — new code, same math, no link-time dependency on the hosted-shared inference engine. Pure addition, no wiring yet — amd64 build-only checkpoint. -
1090d57f— Wired the primitives into the three existing dispatch points identified in the design doc:mama_word_birth(vm_physics_init),mama_word_kill(vm_physics_retire, resolvingvm_idby name first sincecapsule_vm_killtears the entry down), andvm_step/vm_exec/vm_call(vm_physics_touch).vm_physics_tickwired intovm_tick()(vm_runtime.c), gated to Mama's own heartbeat only (sk_get_mama_vm() == vm) since fleet physics is fleet-wide, not per-VM. amd64 QEMU: clean boot, no faults. -
09c5f1cc—VM-CONSERVED?andVM-PHYSICS-STATUSFORTH primitives, registered in both vocabularies alongsideVM-COUNT. -
5be3f251— The mid-implementation design change (see Part 5 below for the full story): kill's heat-return mechanism replaced outright. -
c8af58c7— Design doc rev g, brought back in sync with what was actually implemented. -
9323f776— The big one: migrated every consumer capsule off the old mechanism and deleted it.init.4th: dropped thecompudynamics.4th/fleet-k.4thEXEClines.TRIPOD-TESTrewritten to callVM-CONSERVED?directly — no more artificial tick-pumping before checking conservation, since it now holds continuously by construction rather than needing a warmed rolling average. Dropped theK-KILL-HOOK/K-SPAWN-HOOKcalls around the Hermes kill+rebirth sequence (heat bookkeeping is automatic insideBIRTH/KILLnow).process.4th:SPAWNdrops itsK-SPAWN-HOOKcall;CD-PHASE@deleted (dead code, confirmed no callers, read the now-goneVM-HEAT@/K-TARGETdirectly).hermes/init.4th:EVENT-EMIT's dispatch toHERA-NOTIFY-SPAWN/HERA-NOTIFY-KILL(which called the doomed hooks remotely viaVM-EXEC) removed; both notify words deleted outright.compudynamics.4thandfleet-k.4thdeleted entirely (git rm).- Hit two
mkcapsuleformat limits along the way and fixed them inline: 64-char max line length, 16-content-line max per block (had to trimVM-PHYSICS-STATUSout ofTRIPOD-TEST's body to fit — it's still available standalone, just not auto-printed during acceptance). - amd64 acceptance: full
TRIPOD-TESTpass —PASS: fleet K,PASS: Hermes liveness,PASS: Artemis ready,PASS: reap,K soak— entirely on the new mechanism.
-
8464907a— Full three-arch acceptance (this was the milestone the rev-f rules called for: "after the deletions land, before declaring the design implemented"). amd64/aarch64/riscv64 all pass, with dict_hash matching exactly (0x5115727b8d7cf008) across all three — deterministic parity held through the entire migration. -
7a1383a0— Small doc addendum (see Part 6).
Part 5: The mid-implementation design change — parent-pointer kill
After step 4 above landed (proportional-to-current-heat fan-out across all LIVE survivors on kill), Captain Bob proposed a cleaner replacement mid-session, unprompted by any bug:
"Model it explicitly as a graph: every VM has one outbound edge (parent_vm_id) to whoever birthed it, Tripod VMs (Hera/Hermes/Artemis) are the only nodes with no outbound edge — flagged is_root, not special-cased by name — and kill's heat-return is a single generic function that follows edges until it lands on a root."
This eliminated three problems the proportional formula had at once: it needed division (an all-cold survivor set breaks the split), it needed a "no survivors" fallback, and it invited a weighting-policy argument nobody had actually asked to have.
Investigation before implementing (this mattered): I asked what happens
if a root itself is killed, since the model as stated gave three peers
(Hera/Hermes/Artemis) no outbound edge and therefore no obvious receiver.
Captain Bob's answer — "I'd say [drop it] but I'm questioning why we do that
to begin with? is this a smell?" — sent me back to capsules/init.4th to
check. TRIPOD-TEST (block 2051) deliberately kills and rebirths Hermes as
a resilience test (the "K soak" check) — not leftover boot cruft. That
finding flipped the answer: dropping heat on root-kill would make that exact
test's own conservation check fail (or pass by luck). "Fall through to Hera"
was the option that actually matched the test's intent.
That in turn simplified the whole model further: if every root-kill falls through to Hera specifically, then Hera is the only true root — Hermes and Artemis are just her ordinary children. Captain Bob confirmed: "yes!, Hera is always the only root. later she will be capable of rebirthing of Hermes and Artemis if they fail out" — which is exactly why getting the root-kill case right now matters for real, not hypothetically.
A pleasant surprise during implementation: capsule_birth.c already had
dead scaffolding for exactly this — a parent_vm_id field on the internal
registry node, hardcoded to 0 everywhere, never read anywhere, with the
comment /* VM 0 = Hera for direct children */. Renamed VMRegistryEntry's
unused reserved field to parent_vm_id and exposed it publicly instead of
inventing new state; vm_physics_retire now walks it via the existing
capsule_vm_registry_get, stopping at Hera's self-referential entry
(parent_vm_id == vm_id == 0).
Part 6: Loose thread deliberately left open
Captain Bob, after the three-arch acceptance landed:
"remember that birth is possible for any VM. they have to ask for permission to birth. perhaps that's a question better asked when we start creating messages and the glue utilities to make this an os"
Recorded in the design doc's "Explicitly out of scope" section (final commit,
7a1383a0): today BIRTH is MAMA-only (only Hera's VM has it registered),
so parent_vm_id hardcoded to 0 at every allocation site is correct as
written, not a stand-in for something smarter. The future question — any VM
birthing another, gated by a permission ask rather than by which VM holds the
BIRTH word — is explicitly deferred to when messaging/OS-glue work begins.
parent_vm_id is the field that question will need; it's just not being
generalized yet.
State at session end
- Branch
lithosananke, clean tree, fully pushed. Last commit7a1383a0. - Design doc at rev g, status line: "Implementation underway... amd64-only
acceptance so far, three-arch acceptance still pending" — this line is
now stale, three-arch acceptance completed in commit
8464907aafter the doc's last edit. Update the status line first thing next session. - Every item in the design doc's original "What gets deleted" list is done:
compudynamics.4thgone,fleet-k.4thgone,process.4th'sCD-PHASE@gone,VM-PHYSICS-STATUS/VM-CONSERVED?primitives live and working.
Explicitly not done — pick up here
- Update the design doc's status line to reflect three-arch acceptance is complete (small, immediate, do this first).
capsules/MANIFEST.md(hand-written block registry, "authoritative for capsule block assignments" per its own header) still documents blocks 4400–4404 and other content that no longer exists (K-FLEET,K-SPAWN-HOOK,K-STATUS, etc.). Flagged as doc debt during the migration commit, not fixed. Needs a pass to bring it back in sync.doe-campaign.4this broken (referencesVM-STATUS,VM-INIT,K-BUMP,VM-HERA/VM-HERMES/VM-ARTEMIS, all deleted). This was explicitly flagged in the design doc as its own separate decision (migrate, deprecate, or leave broken until needed) — not part of standard boot, so it doesn't block anything, but it's sitting broken right now.- Hermes's
COMMON-CHfloor (Q.1 3 /, a literal3, not a broken reference) is still hardcoded. Deliberately left alone this session — the fold-in resolution (rev e) already deferred its real fix to Hermes's own sibling physics doc, which doesn't exist yet. - Hermes message-physics and Artemis block-physics sibling design docs — only the decision that they should exist (rev e's fold-in resolution) has been made. Not started.
- DoE rewrite — scoped (four independent metric spaces identified: word, VM-fleet, message, block), not designed or written up as its own doc yet.
- BIRTH-by-any-VM-with-permission — recorded as a future question (Part 6 above), not designed.
- Two Verification-approach items from the design doc were never explicitly
exercised as isolated tests (though the full acceptance run exercises the
spirit of both): the "4th throwaway VM" conservation test, and the
"kill-during-warm-up" dead-
vm_id-in-trajectory test. Worth doing as focused checks before calling the design fully verified, per the doc's own Verification approach section.
Quick orientation for next session
- Read
docs/working/architecture/VM-PHYSICS-DYNAMIC-FLEET-DESIGN-20260705.mdin full (rev g) — it's the living source of truth, this worklog is not. src/starkernel/capsule/capsule_vm_physics.c/.his the new mechanism.git log --oneline 379bf16c..HEADgives the exact commit sequence this worklog describes, in order.