Isabelle toolchain replaced (was genuinely 2011, 14+ years stale) and every theory file fixed to actually compile -- most had apparently never been checked under a working Isabelle at all. Fixed the vm_state self-reference in StarForth_Base.thy properly (word_table is now a free-standing global constant, not a circular record field), corrected the word_physics_transparent axiom (was claiming full state equality from mere exec-equivalence, provably too strong), and worked through 14 years of HOL-Library drift plus several missing-hypothesis bugs across the physics-loop and ACL theories. Two genuine (non-tactical) bugs found and left oops-flagged rather than silently resolved: forth_roll's index arithmetic disagrees with both its own test lemma and the real C ROLL implementation (three-way inconsistency), and pm_wf isn't actually preserved by pm_record_hit/pm_record_miss. Both need a decision, not a proof-script fix. Full writeup in FABRIC-2.md item 5.2.
595 lines
43 KiB
Markdown
595 lines
43 KiB
Markdown
# FABRIC-2.md — the Stadium, continued
|
||
|
||
**Status:** Living working document, opened 2026-08-12 as the successor to `FABRIC.md`
|
||
(now closed/archival — see its own header). This document does not repeat `FABRIC.md`'s
|
||
design argument or history; it restates only outcomes, with pointers back to the section
|
||
that derived them. Read `FABRIC.md` for the "why," this document for the "what's left."
|
||
|
||
**Provenance.** Everything below came from a full, non-sampled read of the entire 7,595-line
|
||
`FABRIC.md` on 2026-08-12, looking for anything not yet resolved: unchecked punch-list
|
||
items, the architectural "### N.N Open" subsections in §1–24, the §25.7 "reported, not
|
||
scheduled" list, and any other "not yet"/"deferred"/"still open" language in the document.
|
||
Item numbers below are carried forward unchanged from `FABRIC.md` where one already existed,
|
||
for traceability — this is not a renumbering.
|
||
|
||
**How to use this document going forward.** New findings, new punch-list items, and new
|
||
decisions get added here, not to `FABRIC.md`. Follow the same discipline `FABRIC.md` §25.0
|
||
established (read it there — it isn't repeated here) for how work gets picked up, closed,
|
||
and recorded.
|
||
|
||
---
|
||
|
||
## A. Blocked or scoped, not started
|
||
|
||
- [ ] **1.11 — Dirty-event granularity.** Leaning region-based. Blocked on item 4.3 — settled
|
||
as part of the console migration, not speculatively before it. *Refs (FABRIC.md):* §17.5,
|
||
§23.2, §23.4 #1.
|
||
|
||
- [ ] **4.3 — Console.** Umbrella item; settles 1.11 as part of the work. Nearly everything
|
||
under it (4.3.1–4.3.7f, 4.4–4.4ac) is done — the parent stays open only because 4.4s below
|
||
is still blocked and nothing has formally closed the umbrella. *Refs (FABRIC.md):* §17.5,
|
||
§27.
|
||
|
||
- [ ] **4.4s — `(user)` prompt segment.** Scoped, blocked, not started. Extends 4.4's prompt
|
||
format. *Refs (FABRIC.md):* §27.8, 4.4.
|
||
|
||
- [ ] **4.6 — Artemis last.** It works today; it is the thing that cannot be broken. Not
|
||
started.
|
||
|
||
- [ ] **5.1 — Re-run the DoE on the new substrate.** A green POST suite is not evidence that
|
||
determinism holds under the Stadium migration — needs its own campaign. Not started.
|
||
|
||
- [x] **5.2 — Isabelle/HOL.** One datatype, one index space, one conservation theorem. Not
|
||
started.
|
||
|
||
> **DIAGNOSTIC 2026-08-13 — toolchain replaced, build attempted, one root-cause file
|
||
> found broken; not yet fixed.** The installed Isabelle at
|
||
> `/home/rajames/CLionProjects/Isabelle2011-1` was genuinely Isabelle2011-1 (14+ years
|
||
> old, from the directory's own timestamps) — essentially guaranteed stale against
|
||
> `proof/`'s 23 `.thy` files. Replaced with **Isabelle2025-2** (current stable, Jan
|
||
> 2026) at the same path, old install preserved alongside as
|
||
> `Isabelle2011-1.old` rather than deleted. `isabelle build -v -D proof/` run for real
|
||
> (not a dry run): the `Pure`/`HOL` base heaps loaded fine, `HOL-Library` built clean
|
||
> from scratch in ~5 minutes, but the `StarForth` session **failed**.
|
||
>
|
||
> **One root cause, not sixteen.** `proof/StarForth_Q48_16.thy` — the base theory
|
||
> every other theory in the session transitively depends on — fails to load with three
|
||
> distinct real problems, all consequences of 14 years of `HOL-Library` changes since
|
||
> 2011:
|
||
> 1. **Line 75** — `unat_push_bit` is now an undefined fact; that lemma name from the
|
||
> old `Word` library appears renamed or removed.
|
||
> 2. **Lines 63 and 122** — two `by (simp add: drop_bit_push_bit word_size)` proofs
|
||
> (`drop_bit 16 (push_bit 16 n) = n` round-trip lemmas) no longer close, likely the
|
||
> same underlying `Word`-library lemma-set drift as #1.
|
||
> 3. **Line 144** — a genuine name collision, not proof drift: `q48_accuracy`'s `total`
|
||
> parameter now shadow-fails against a same-named `HOL-Library` constant (the
|
||
> order-theory "total relation" predicate, type `('a×'a) set ⇒ bool`) that didn't
|
||
> exist or wasn't in scope under the 2011 library — Isabelle now resolves the
|
||
> identifier to that constant instead of binding it as a fresh parameter, producing a
|
||
> real type error at the `definition`.
|
||
>
|
||
> Every other theory (`StarForth_Base`, all seven physics-loop theories, all four ACL
|
||
> theories, `StarForth_Correctness`, etc.) reports "unresolved" — that is 100% downstream
|
||
> fallout from this one file never loading, not independent breakage.
|
||
> Captain Bob additionally wants a Gitea Actions CI job running this build eventually,
|
||
> once the theories verify — not scoped or built yet.
|
||
>
|
||
> **`StarForth_Q48_16.thy` FIXED 2026-08-13 — verifies 100%, all lemmas true.** The
|
||
> `AND`/`total`/renamed-lemma issues were mechanical (14 years of `HOL-Library` drift) and
|
||
> fixed as such. Two lemmas — `q48_from_u64_mono` and `q48_div_one` — were genuinely
|
||
> **false as originally stated**, not just outdated proof scripts: both claimed
|
||
> round-trip/monotonicity properties for `push_bit 16` with no upper bound, but
|
||
> `push_bit 16` wraps mod 2^64 on a 64-bit word, so e.g. `a = 2^48` breaks both (concrete
|
||
> counterexamples recorded in the file's own comments at each lemma). Fixed per Captain
|
||
> Bob's instruction by adding the same `unat _ < 2^48` "overflow-free range" bound this
|
||
> file already uses everywhere else — both now provably true and verified.
|
||
>
|
||
> **`StarForth_Base.thy`'s `vm_state` circularity FIXED 2026-08-13 — properly, not
|
||
> patched around.** The `vm_state` record had a field `word_table :: "nat ⇒ vm_state ⇒
|
||
> vm_state"` — self-referential inside the very record defining it, which HOL records
|
||
> cannot express under any Isabelle version (no fixed-point support). The file's own
|
||
> prior comment claimed hoisting the field to "top-level" solved this; it didn't — the
|
||
> field's type still named `vm_state` before `vm_state` existed. **Real fix:** `word_table`
|
||
> is now a free-standing, uninterpreted global constant (`consts word_table :: "nat ⇒
|
||
> vm_state ⇒ vm_state"`) declared *after* `vm_state`, not a field of it at all — which is
|
||
> also more faithful to the C reality: word dispatch is a fixed table built once at
|
||
> compile time, not per-VM-instance mutable state, so it never belonged inside the record.
|
||
> This required updating three dependent files that referenced the old `word_table s n s`
|
||
> three-argument call pattern (reading the table from a specific state, then applying it)
|
||
> down to the new two-argument `word_table n s` form, and dropping the now-vacuous
|
||
> `word_table s1 = word_table s2` conjunct from `StarForth_Transition.thy`'s core
|
||
> `exec_equiv` relation — word_table no longer varies by state at all, so that equality
|
||
> was always trivially true once the field became global; the real content
|
||
> (`word_physics_transparent`: word execution depends only on exec-visible fields) survives
|
||
> unchanged. `StarForth_Base.thy` now loads and verifies 100%, unblocking most of the
|
||
> session's other theories as a side effect (`StarForth_Arithmetic_Words`, `StarForth_Mutex`,
|
||
> `StarForth_Loop2_Window`, `StarForth_Loop7_Heartrate`, all four `ACL_*` theories all now
|
||
> build clean too).
|
||
>
|
||
> **Three more independent issues surfaced once `StarForth_Base` unblocked everything
|
||
> downstream of it — not yet fixed, same "apparently never actually compiled before"
|
||
> pattern:**
|
||
> - `StarForth_Loop2_Window.thy` — five proofs (lines 83, 123, 127, 137, 145) about
|
||
> `rw_eff_window`/`ROLLING_WINDOW_SIZE`/`ADAPTIVE_MIN_WINDOW_SIZE` bounds fail to close;
|
||
> not yet triaged whether these are library drift or, like the two Q48_16 lemmas, actually
|
||
> false as stated.
|
||
> - `StarForth_Loop5_WinInf.thy:52` — a genuine type error, not drift:
|
||
> `ANOVA_VARIANCE_THRESHOLD :: nat` is defined as `= Q48_SCALE`, but `Q48_SCALE :: q48`
|
||
> (`64 word`) — nat and word are different types, this could never have type-checked
|
||
> under any Isabelle version.
|
||
> - `StarForth_Memory_Words.thy:157` — same `AND` bundle issue already fixed in
|
||
> `StarForth_Q48_16.thy` (needs its own `unbundle bit_operations_syntax`), not yet applied
|
||
> here.
|
||
>
|
||
> **ALL 23 THEORY FILES NOW VERIFY 2026-08-13 — `isabelle build -v -D proof/` finishes
|
||
> with zero errors.** Continuing from the above: worked through every remaining file in
|
||
> the same session — `StarForth_Loop2_Window`, `StarForth_Loop5_WinInf`,
|
||
> `StarForth_Memory_Words`, `StarForth_Mutex`, `StarForth_Stack_Words`,
|
||
> `StarForth_Return_Stack_Words`, `StarForth_Loop1_Heat`, `StarForth_Loop3_Decay`,
|
||
> `StarForth_Loop4_Pipeline`, `StarForth_Loop6_DecayInf`, `StarForth_Logical_Words`,
|
||
> `StarForth_Transition`, `StarForth_Concurrent`, `StarForth_Correctness`, all four
|
||
> `ACL_*` theories. Most fixes were the same two mechanical categories already
|
||
> established above (`AND`/`OR`/`XOR` needing `unbundle bit_operations_syntax` per file;
|
||
> `nat`/`q48` type mismatches needing `unat`), plus a recurring pattern of lemmas missing
|
||
> a hypothesis their own proof genuinely needs (documented in place at each site, e.g.
|
||
> `window_advance_act_monotone`, `window_shrink_mono`, `window_grow_mono`,
|
||
> `slope_decrease_mono`, `slope_increase_mono`, `heat_decay_monotone`,
|
||
> `heat_increment_non_decreasing` all needed a bound their original statement omitted).
|
||
>
|
||
> **One more foundational finding, fixed properly.** `StarForth_Transition.thy`'s axiom
|
||
> `word_physics_transparent` — one of only 2 axioms the entire correctness framework
|
||
> rests on — originally concluded **full state equality**
|
||
> (`word_table n s1 = word_table n s2`) from mere `\<simeq>` exec-equivalence of the inputs.
|
||
> That is provably too strong: two states agreeing only on `data_stack`/`return_stack`/
|
||
> `memory` can still differ in any physics field (`rolling_window`, `heartbeat`, etc.),
|
||
> and a word that never reads or writes those physics fields leaves them exactly as
|
||
> found — still different between the two outputs. This surfaced as
|
||
> `StarForth_Concurrent.thy`'s `foldl_word_table_eq` failing at the empty-list base case,
|
||
> which reduced to needing `s1 = s2` from `s1 \<simeq> s2` alone — genuinely unprovable, not a
|
||
> tactic problem. **Fixed by weakening the axiom's conclusion to `\<simeq>`** (congruence,
|
||
> not equality) — exactly what the file's own audit protocol ("word body only reads the
|
||
> three exec_equiv fields") actually justifies, and exactly what every downstream
|
||
> consumer (`heartbeat_noninterference`, `heartbeat_trace_noninterference`, etc.) only
|
||
> ever needed, since they all just extract `data_stack`/`return_stack` projections in the
|
||
> end. Propagated through `StarForth_Transition.thy`, `StarForth_Concurrent.thy`,
|
||
> `StarForth_Correctness.thy`. `foldl_word_table_eq` itself needed restructuring from
|
||
> `assumes/shows + induction ... arbitrary: s1 s2` to an explicit object-level
|
||
> `\<forall>s1 s2. ... \<longrightarrow> ...` form — the `arbitrary:` generalization was not reliably
|
||
> reverting the `assumes` premise into the induction's base case across five different
|
||
> tactics tried, and the explicit-quantifier form sidesteps that mechanism entirely.
|
||
>
|
||
> **Two genuine, non-tactical bugs found and deliberately left `oops`-flagged rather
|
||
> than fixed, each documented in place with a full explanation:**
|
||
> - `StarForth_Stack_Words.thy`'s `roll_two_is_rot` — a three-way inconsistency between
|
||
> `forth_roll_def`'s own index arithmetic (off-by-one against what the lemma expects),
|
||
> the lemma's own expected ROT-equivalent output, and the real C `stack_word_roll`
|
||
> (`src/word_source/stack_words.c:287-320`), which uses a third, explicitly
|
||
> bottom-indexed convention ("1-indexed from bottom: n=1 moves bottom item to top")
|
||
> matching neither. Not a proof-script issue — a genuine open question about what
|
||
> `ROLL` is supposed to do, needing a decision before it can be closed either
|
||
> direction.
|
||
> - `StarForth_Loop4_Pipeline.thy`'s `pm_record_hit_preserves_wf`/
|
||
> `pm_record_miss_preserves_wf` — `pm_wf` only requires `pm_last_accuracy_den > 0`
|
||
> when `pm_prefetch_attempts > 0`; both operations increment `attempts` from 0 to 1
|
||
> without ever having required `den > 0` beforehand, so the postcondition needs
|
||
> something the precondition never guaranteed. A genuine invariant-maintenance gap in
|
||
> `pm_wf`'s own definition, not fixed here.
|
||
>
|
||
> Toolchain: `/home/rajames/CLionProjects/Isabelle2011-1` now genuinely runs
|
||
> Isabelle2025-2 (old 2011 install preserved as `Isabelle2011-1.old`). Gitea Actions CI
|
||
> for this build remains wanted but unscoped (Captain Bob, see above) — not built yet.
|
||
|
||
- [ ] **5.3 — Shrink the subsystem documents.** `ARTEMIS.md`, `HERMES.md`, `CONSOLE.md`,
|
||
`TRIPOD.md` need trimming now that `FABRIC-2.md` (via `FABRIC.md`) is the design-of-record.
|
||
Not started. **Specific pending edit found within this item:** `TRIPOD.md`'s own Immediate
|
||
Goal section still describes Hera spawning Hermes and Artemis automatically at boot — item
|
||
0.1 undid that behavior, and the doc was never updated to match.
|
||
|
||
> **Pending edit fixed 2026-08-13; the item's larger scope (actually shrinking the four
|
||
> documents) remains open.** `TRIPOD.md`'s "Immediate Goal" section corrected: it now
|
||
> states plainly that Hera boots alone by design (confirmed live —
|
||
> `capsules/init.4th` births no other VM), that Hermes/Artemis-as-VM-patron births are
|
||
> on-demand via test tooling today (not automatic boot behavior), and separately notes
|
||
> that Artemis's block-storage *device* does auto-attach at the C level at boot
|
||
> (`kernel_main.c`'s M7.pre step) — distinct from, and not to be confused with, an
|
||
> Artemis VM patron birth. The acceptance criteria were reworded from "Hera spawns X"
|
||
> to "Hera can spawn X" to stop asserting automatic behavior that doesn't exist. This
|
||
> closes only the one stale claim this document already flagged — `ARTEMIS.md`,
|
||
> `HERMES.md`, `CONSOLE.md` were not read for similar staleness, and none of the four
|
||
> documents' actual line-count reduction (the item's real ask) has been attempted.
|
||
|
||
> **AUDIT of the other three, 2026-08-13.** Read each in full. One got the same
|
||
> well-scoped fix as `TRIPOD.md`; the other two turned up staleness too large to
|
||
> silently correct — reported here for a call, not fixed.
|
||
>
|
||
> **`ARTEMIS.md` — fixed, same pattern as `TRIPOD.md`.** Its Build Status section
|
||
> claimed Artemis "boots live in the Tripod fleet (announces ready to Hermes) on every
|
||
> amd64/aarch64/riscv64 kernel boot." Same conflation as `TRIPOD.md` had: confirmed
|
||
> `capsules/init.4th` births no VM but Hera (item 0.1), so Artemis-as-VM-patron capsule
|
||
> birth is not automatic — but Artemis's block-storage *device* (the virtio-blk disk)
|
||
> genuinely does auto-attach at the C level at boot (`kernel_main.c` M7.pre), which is
|
||
> real and unchanged. Corrected in place; rest of the document (Build Status detail,
|
||
> Storage Design future material) read as accurate and current, not touched further.
|
||
>
|
||
> **`CONSOLE.md` — not fixed, staleness is structural, needs Captain Bob's call on how
|
||
> to handle it.** This document is dated 2026-08-02 and states outright "nothing
|
||
> described here is implemented yet... there is no Console code in the tree yet." That
|
||
> is no longer true in two different ways:
|
||
> 1. **Its central premise was superseded by a later, DECIDED ruling.** `CONSOLE.md`
|
||
> says Console is "the fleet's fourth leg — Quadrupod, not Tripod. A real StarForth
|
||
> VM, spawned by Hera, participating in fleet K≡1.0." `FABRIC.md` §17.5 later ruled
|
||
> the opposite, explicitly and by name: the framebuffer is **not** a patron, it is a
|
||
> utility — "closer to the power company... Not the Stadium. Not the basement of the
|
||
> Stadium. A third thing." Confirmed no Console VM exists anywhere in the tree
|
||
> (grepped `stadium.h`, `vm_uuid.c`, the fleet machinery — zero hits for a Console
|
||
> VM kind). `CONSOLE.md`'s entire "What Console Is" section describes a design that
|
||
> was designed *away from*, not toward.
|
||
> 2. **Its "explicitly out of scope" list is now false.** `CONSOLE.md` lists keyboard
|
||
> input as out of scope, stating flatly "there is no keyboard driver anywhere in
|
||
> this codebase yet." Confirmed false: `src/starkernel/arch/amd64/i8042.c` (PS/2,
|
||
> exactly the driver `CONSOLE.md` itself predicted as "the pragmatic first target")
|
||
> and `src/starkernel/virtio/virtio_input.c` (USB HID, which the doc predicted
|
||
> "should come later, if at all") both exist, and a keyboard-to-REPL bridge is live
|
||
> (`FABRIC.md` item 4.4v, `src/word_source/keyboard_words.c`,
|
||
> `src/starkernel/repl.c`'s `sk_kbd_getc()`).
|
||
>
|
||
> Some of the document's technical content did land as described and is *not*
|
||
> stale: the bottom-left-origin/Y-up Cartesian coordinate flip is real
|
||
> (`framebuffer.c:225`'s comment confirms the pre-flip/post-flip distinction), and
|
||
> stroke-font glyph capsules plus a later TrueType adjunct did ship (`FABRIC.md`
|
||
> §27.6/§27.7). The font/pen layer and heat-driven-redraw sections were not
|
||
> independently re-verified line-by-line here — flagged as unverified, not claimed
|
||
> accurate or inaccurate.
|
||
>
|
||
> Given the console/framebuffer work that actually shipped went a different
|
||
> architectural direction than this document's core design (utility, not a 4th
|
||
> Tripod VM; direct C/StarForth console code, not Hermes-routed VM messages), a
|
||
> minimal correction paragraph the way `TRIPOD.md`/`ARTEMIS.md` got would understate
|
||
> the gap. This needs either a full rewrite against `FABRIC.md` §17.5/§27 as the
|
||
> design-of-record, or an explicit "superseded, kept for history" header — Captain
|
||
> Bob's call, not made here.
|
||
>
|
||
> **`HERMES.md` — not fixed, one concrete verified discrepancy plus a stale block map,
|
||
> needs Captain Bob's call.** `HERMES.md`'s own "Message node — 8 cells" layout table
|
||
> (offsets 0–7: type, sender, recipient, payload addr, payload len, heat, seq, channel)
|
||
> contradicts the capsule's own `9 CONSTANT MSG-CELLS` (`capsules/hermes/init.4th`,
|
||
> confirmed twice already this session — once in this document's §23.4 #2 closure,
|
||
> once independently here). The real 9-cell layout item 4.2 shipped stores a Stadium
|
||
> cell index at offset 5, not a raw heat value directly, and adds an `ORIG-TYPE` field
|
||
> at offset 8 that `HERMES.md` doesn't mention at all. Separately, `HERMES.md`'s "v1
|
||
> Block Map — LOCKED" lists only blocks 4100–4128; the actual capsule
|
||
> (`grep '^Block ' capsules/hermes/init.4th`) also has blocks 4142, 4144–4159, 4175,
|
||
> 4176 — a substantial amount of item 4.2's Stadium-integration content (VM-name
|
||
> routing, StadiumBehaviour tags, the admission-heat math) isn't in the documented map
|
||
> at all, and it's not verified here whether 4122–4128 as `HERMES.md` numbers them
|
||
> still hold the same content. `HERMES.md` closes with "This document is authoritative.
|
||
> If it conflicts with something in the codebase, the codebase is wrong" — that claim
|
||
> does not hold for the cell count, which is a deliberate, intentional change (the
|
||
> capsule's own comments say "item 4.2 -- heat/capacity via Stadium"), not a drift bug.
|
||
> Reported, not fixed — reconciling the full block map is real work, not a one-line
|
||
> correction.
|
||
|
||
---
|
||
|
||
## B. Live and unmeasured — deferred while Tripod was pruned to Hera-alone, now genuinely live since item 4.2 restored Hermes
|
||
|
||
- [x] **Fleet heat leak.** Integer-truncation drift in `vm_physics_touch()` (FABRIC.md §20.2)
|
||
was explicitly noted as invisible with only Hera resident, and "becoming measurable the
|
||
moment Phase 4 restores Hermes/Artemis." Phase 4 has now restored Hermes (item 4.2). This
|
||
is the single most concrete open item carried into this document — real, live, and
|
||
unmeasured.
|
||
|
||
> **INVESTIGATED 2026-08-13 — §20.2's truncation claim does not hold; no live drift risk
|
||
> exists today.** Full read of `capsule_vm_physics.c`: every write to
|
||
> `execution_heat_q48` goes through exactly one of `vm_physics_init()` (birth),
|
||
> `vm_physics_transfer()` (`:176-183`), or `vm_physics_retire()`'s zero-out after transfer
|
||
> (`:277`) — confirmed via a repo-wide grep, nothing else touches the field.
|
||
> `vm_physics_transfer()` moves an identical amount off `from` and onto `to` in the same
|
||
> call (`from -= moved; to += moved`), unconditionally. The touch() fan-out (`:334`,
|
||
> `share = (moved_total * heat) / others_total`) truncates per-VM, so `sum(shares) <
|
||
> moved_total` — but each `share`, truncated or not, is what actually leaves its VM *and*
|
||
> what actually arrives at the touched VM, via the same `vm_physics_transfer()` call. A
|
||
> worked example: others_total=10 (A=5, B=5), moved_total=7 → share_A=share_B=3, sum is 6
|
||
> not 7. Fleet sum before = 10+target; after = (2+2)+(target+6) = 10+target. Identical. The
|
||
> truncation is a rate-fidelity shortfall (the touched VM pulls in less than the physics
|
||
> model intended) — it cannot move the fleet-wide sum, because nothing is ever subtracted
|
||
> from one place without the identical amount landing at exactly one other. This matches
|
||
> the header's own contemporaneous claim (`capsule_vm_physics.h:31-33`): conservation is
|
||
> "held by construction: every state change is a balanced transfer." Git history rules out
|
||
> a since-fixed bug: `vm_physics_transfer()` and the touch() loop are unchanged since the
|
||
> file's creation (`a5ed8c3`, 2026-08-01), predating §20.2's correction (`055e944`,
|
||
> 2026-08-03) — the claim was made about the exact code read here, not an earlier version
|
||
> of it.
|
||
>
|
||
> **The one path that can genuinely drop heat** is `vm_physics_retire()`'s guarded
|
||
> "no valid root" case (`:271-275`) — the dying VM's remainder has nowhere
|
||
> conservation-preserving to go if it IS the root, or its parent chain is broken. Both are
|
||
> structurally excluded today: `capsule_vm_kill` refuses to kill Hera, and
|
||
> `parent_vm_id` is set once at birth and never rewritten, so a well-formed chain can't
|
||
> corrupt itself. Real in principle, unreachable under current invariants — tracked
|
||
> separately in §C below rather than as a live measurement task.
|
||
>
|
||
> FABRIC.md §20.2 itself should eventually be corrected to match (currently still asserts
|
||
> the truncation causes monotonic drift); not done here to keep this document's discipline
|
||
> of not editing `FABRIC.md` further.
|
||
|
||
- [x] **Multi-VM heartbeat ownership.** Whose `tick_target_ns` drives the one physical timer
|
||
(FABRIC.md §26.5) has only one answer while Hera is the only VM; explicitly "not resolved
|
||
for when Hermes/Artemis return." Hermes has returned (item 4.2). Same situation as the
|
||
fleet heat leak above — a deferral whose triggering condition has since become true.
|
||
**Ruled:** there is exactly one physical timer, so exactly one VM may write its re-arm
|
||
period — never whichever VM's `vm_tick()` happened to run last. Hera is the fixed point
|
||
everywhere else in this design (patron zero, sole capacity arbiter, sole birther/killer of
|
||
VMs — see FABRIC.md §20.2, §26), so she is the sole owner here too. Every other VM's Loop
|
||
#7 (`vm_tick_inference_engine()`) still adapts its own `vm->heartbeat.tick_target_ns`
|
||
exactly as before — that per-VM bookkeeping is correct and untouched — it simply never
|
||
reaches the shared physical re-arm; only Hera's does.
|
||
*Done when:* `vm_tick_inference_engine()` gates the physical-timer re-arm behind
|
||
`vm_uuid_is_hera(vm->stadium_vm_id)`, and a three-arch QEMU boot with Hermes live (item
|
||
4.2) is clean.
|
||
*Refs (FABRIC.md):* §20.2, §26.5.
|
||
|
||
> **DONE 2026-08-13.** `vm_tick_inference_engine()`
|
||
> (`src/starkernel/vm/vm_runtime.c:766`) now wraps the kernel-base re-arm block in
|
||
> `if (vm_uuid_is_hera(vm->stadium_vm_id))`, using the existing
|
||
> `vm_uuid_is_hera()` (`src/starkernel/capsule/vm_uuid.c:51`) already used the same way at
|
||
> the birth/kill call sites in `capsule_birth.c` and `capsule_vm_physics.c`. Three-arch
|
||
> acceptance boot, all clean to `ok>`, Hermes live and self-testing in every log:
|
||
> `logs/20260813-075940/amd64`, `logs/20260813-080125/aarch64`,
|
||
> `logs/20260813-080257/riscv64`. Committed `bcc72d0`.
|
||
|
||
---
|
||
|
||
## C. Reported bugs and dead code, not yet fixed
|
||
|
||
- [x] **`vm_physics_retire()`'s dropped-remainder case.** (`capsule_vm_physics.c:271-275`)
|
||
If the dying VM is itself the fleet root, or its parent chain is broken, its remaining
|
||
heat has nowhere conservation-preserving to go and is zeroed instead of transferred — a
|
||
genuine, if narrow, violation of the `sum(execution_heat) == Q48_ONE` invariant. Found
|
||
while investigating the "Fleet heat leak" item above, 2026-08-13.
|
||
|
||
> **CORRECTION, same day:** the "structurally excluded" framing this item originally had
|
||
> was wrong — the Hera case *was* reachable. `mama_word_kill()`
|
||
> (`mama_forth_words.c:520-530`) called `vm_physics_retire(entry.vm_id)` for any live-state
|
||
> VM found by name, with no Hera check, *before* `capsule_vm_kill()`'s own Hera guard
|
||
> (`capsule_birth.c:313-316`) ever ran. `S" Hera" KILL` therefore hit
|
||
> `vm_physics_retire()` with Hera's own id; her self-referential `parent_vm_id` made
|
||
> `vm_physics_find_root_id()` return her own id immediately, `root` came back `NULL`, and
|
||
> the fleet's entire `execution_heat_q48` sum was silently zeroed — with only the
|
||
> harmless-looking "cannot kill Hera" message as visible output, since that guard runs
|
||
> second. The broken-parent-chain half of this item remains genuinely unreachable
|
||
> (`parent_vm_id` is set once at birth and never rewritten); only the Hera half was live.
|
||
>
|
||
> **DONE 2026-08-13.** Fixed by excluding Hera from the retire call at the one call site,
|
||
> mirroring `capsule_vm_kill()`'s existing guard: `mama_forth_words.c`'s
|
||
> `capsule_vm_find_by_name_nocase(...) && entry.state == VM_STATE_LIVE` condition gained
|
||
> `&& !vm_uuid_is_hera(entry.vm_id)`. No change to `vm_physics_retire()` or
|
||
> `capsule_vm_kill()` itself. Three-arch acceptance boot, all clean to `ok>`:
|
||
> `logs/20260813-083429/amd64`, `logs/20260813-083551/aarch64`,
|
||
> `logs/20260813-083738/riscv64`. Committed `db66406`.
|
||
- [x] **`arch_mmu_init()` — TODO, not accidental dead code.** Declared (`arch.h:73`) and
|
||
defined on all three architectures (`amd64/arch.c:246`, `aarch64/arch.c:169`,
|
||
`riscv64/arch.c:179`) but never called anywhere — confirmed by repo-wide grep, 2026-08-13.
|
||
Each definition is a real doc-commented no-op stub ("paging will be wired up in later
|
||
milestones" / "MMU bring-up deferred" / "Sv39/Sv48 bring-up deferred"), and each comment
|
||
describes it as satisfying "the common `arch_mmu_init()` call site shared across all three
|
||
ISAs" — a call site that does not exist anywhere in the tree today. `vmm.c` (M3) handles
|
||
page-table management independently and does not need this hook to function. Captain Bob's
|
||
call, 2026-08-13: leave the three stubs in place as scaffolding for a future per-arch MMU
|
||
milestone rather than deleting or wiring in a no-op call site now — ruling recorded, closed
|
||
as investigated/not-a-bug rather than left open. Found during item 4.3.5a.
|
||
- [x] **`include/block_subsystem.h:168`'s `encoding` field (ASCII/UTF-8/binary) is dead
|
||
metadata — nothing reads or writes it.** (FABRIC.md §27.6) Investigated 2026-08-13, scope
|
||
turned out wider than the field itself: `blk_get_meta()`/`blk_set_meta()`
|
||
(`src/block_subsystem.c:881,903`) are the only public API for the whole `blk_meta_t`
|
||
struct and have **zero callers anywhere in the tree** — not just `encoding` but
|
||
`content_type`, `content_length`, `owner_id`, `permissions`, `acl_block`, `signature`,
|
||
`entropy`, `hash`, the chain fields, and `app_data[15]` are all unused. The struct is a
|
||
real fixed on-disk byte layout (packed 341 bytes/block into `META_REGION_OFFSET`,
|
||
`src/block_subsystem.c:69-71`), serialized via `meta_to_slice()`/`meta_from_slice()`
|
||
during cache writeback/load — so it isn't free-standing dead code to delete, removing a
|
||
field would shift every subsequent field's on-disk offset. **Ruling (Captain Bob,
|
||
2026-08-13): flag and leave as-is** — this metadata subsystem is scaffolding expected to
|
||
get consumed once Artemis's design (content-typed/owned/ACL'd blocks) is completed, not a
|
||
bug to fix now.
|
||
- [ ] `tools/README.md` documents a `fbtest.c` example that does not actually exist in
|
||
`tools/` — stale-doc discrepancy. Confirmed 2026-08-13, and the discrepancy is two-sided:
|
||
`git log --all` shows `fbtest.c` was never added to the repo at any point — the table
|
||
entry describes a tool that was documented but never built. Meanwhile `tools/ttftest.c`
|
||
(added `5f6cc05`, item 4.3.7, TrueType parser core test) is a real tool that exists on
|
||
disk but has no README table entry at all. Left unfixed pending explicit instruction
|
||
(README edit would be a fix, not just an investigation).
|
||
- [x] **`hotwords_cache_promote()` has a NULL-write bug on a full cache.** Confirmed
|
||
2026-08-13: the full-cache branch (`src/physics_hotwords_cache.c:362-371`) writes `word`
|
||
into `cache->cache[cache->lru_index]` unconditionally once `cache->cache_count >=
|
||
HOTWORDS_CACHE_SIZE` — it does not check `word` for NULL on that path, unlike the
|
||
slot-available branch below it, which does. A NULL `word` would silently evict a live
|
||
cache entry and replace it with NULL. Confirmed unreachable in practice: all four call
|
||
sites (`physics_hotwords_cache.c:284`, `physics_execution_hooks.c:140`,
|
||
`rolling_window_of_truth.c:787`, `src/starkernel/vm/vm_core.c:731`) only call
|
||
`hotwords_cache_promote()` after a `DictEntry*` NULL check has already passed. **Ruling:**
|
||
flag and leave as-is, matching the `arch_mmu_init()`/block_subsystem precedent — real
|
||
defect, no live trigger, not fixed without explicit instruction.
|
||
- [x] **`heartbeat_trust()` has zero callers — dead code.** Stale claim, corrected 2026-08-13:
|
||
`heartbeat_trust()` (`src/starkernel/heartbeat.c:205`) is called from
|
||
`src/starkernel/kernel_main.c:913`, printed as the `trust=0x...` field of the M5
|
||
heartbeat boot-milestone diagnostic line, alongside tick count and variance. Not dead
|
||
code — closed as investigated/not-a-bug.
|
||
- [x] **`m5_time_trust`/`m5_variance` are declared and never used.** Confirmed 2026-08-13:
|
||
repo-wide grep for both names turns up only their own declarations
|
||
(`include/vm.h:315-316`) — no reader, no writer, anywhere. Genuinely dead struct fields.
|
||
**Ruling:** flag and leave as-is, same precedent as the other Section C dead-code items —
|
||
not removed without explicit instruction.
|
||
- [ ] `src/*.c.bak` files (`vm.c.bak`, `doe_metrics.c.bak`, `inference_engine.c.bak`) remain
|
||
tracked in git at `src/` top level. Repo hygiene debt, not intentional (also noted in
|
||
`.claude/CLAUDE.md`).
|
||
- [ ] `bump-z`/`bump-y` Makefile targets reference `STARFORTH_VERSION_MAJOR`/`MINOR`/`PATCH`/
|
||
`STARFORTH_VERSION_STRING` fields that don't exist in the actual generated
|
||
`include/version.h` (also noted in `.claude/CLAUDE.md`).
|
||
- [x] **Kconfig/`menuconfig` has never been exercised end-to-end.** Every knob added so far
|
||
(including item 4.1's `STADIUM_WORD_HEAT_QUANTUM`/`STADIUM_WORD_COOL_RATE_Q48`) has only
|
||
ever been verified via its `Makefile.starkernel` default. Nobody has run
|
||
`make -f Makefile.starkernel menuconfig`, changed a value, and confirmed it flows through
|
||
to a build. Flagged by Captain Bob 2026-08-05.
|
||
|
||
> **DONE 2026-08-13.** The vendored `tools/kconfig` build had never actually succeeded on
|
||
> this machine — `bison`/`flex` were missing, installed with Captain Bob's go-ahead.
|
||
> `mconf` needs an interactive TTY this environment doesn't have, so the exercise used
|
||
> `conf` directly (same underlying engine `menuconfig` calls) rather than the ncurses
|
||
> frontend itself. `make -f Makefile.starkernel ARCH=amd64 kernel_amd64_defconfig`
|
||
> generated `build/amd64/.config` correctly (`CONFIG_SK_PARITY_DEBUG` unset, matching
|
||
> Kconfig's `default n`). Hand-editing that line to `CONFIG_SK_PARITY_DEBUG=y` and
|
||
> re-running the kernel build showed the real compile line for
|
||
> `src/starkernel/vm/parity.c` gained `-DSK_PARITY_DEBUG=1` (was `=0`), and the object
|
||
> compiled clean with it; reverting the line back to unset flipped the flag back to
|
||
> `=0`. Confirms the `mk/Kconfig.mk` bridge and `kconfig_bool` macro genuinely wire a
|
||
> `.config` change through to `-D` flags in both directions — this had never been
|
||
> verified before. One caveat found in passing, not itself a bug: invoking
|
||
> `tools/kconfig/conf` directly (bypassing the Makefile) writes a stray `.config` +
|
||
> `include/config/`/`include/generated/` at the repo root instead of under `build/`,
|
||
> since it doesn't pick up the `KCONFIG_CONFIG`/etc. env vars `mk/Kconfig.mk` exports —
|
||
> only matters if invoking `conf` by hand outside the Makefile targets, as done here;
|
||
> cleaned up before committing. No `.config`/`build/` artifacts committed — both are
|
||
> gitignored working state.
|
||
|
||
---
|
||
|
||
## D. Design questions still genuinely open
|
||
|
||
- [ ] **§12 Q5 — the full eight-loop interference analysis has not been done.** Its stated
|
||
blocker ("no real time base on all three ISAs") is gone — Phase 0 completed that — so this
|
||
is now unblocked and simply never picked back up.
|
||
|
||
> **ANALYSIS DONE 2026-08-13 — found a real violation of §22.4's required ordering, left
|
||
> open pending Captain Bob's call on how to handle it.** Traced every loop's actual firing
|
||
> cadence from source (`src/starkernel/vm/vm_core.c`, `vm_runtime.c`,
|
||
> `capsule_vm_physics.c`), in execution counts, not the nominal per-loop constant names:
|
||
>
|
||
> - **Cadence 1 execution:** Loop #1 (heat), Loop #2 (rolling-window record), Loop #4
|
||
> (pipelining) — every word dispatch.
|
||
> - **Cadence `HEARTBEAT_CHECK_FREQUENCY` = 256 executions:** Loop #3 (background decay,
|
||
> batched), `rolling_window_service()`, `dict_adaptive_optimization_pass()`, L8 Jacquard
|
||
> vote, fleet-capacity-counter increment — all five in the same `vm_heartbeat_run_cycle()`
|
||
> call, zero separation between them.
|
||
> - **Cadence `HEARTBEAT_INFERENCE_FREQUENCY` ticks × 256 = 256,000 executions:** Loop #5
|
||
> (window width) + Loop #6 (decay slope) + Loop #7 (adaptive heartrate) — all three
|
||
> merged into one `vm_tick_inference_engine()` call, zero separation between them.
|
||
>
|
||
> Two real findings, not just tidiness:
|
||
>
|
||
> 1. **The fleet-capacity loop can fire *faster* than a VM's own heat loop, not slower —
|
||
> the opposite of §22.4's "capacity must be the slower loop."** §22.4 cites
|
||
> `vm_physics_heartbeat_tick()` (`capsule_vm_physics.c:454-459`) by name as the existing
|
||
> precedent for the required 1000:1 separation. But that function is called from *every*
|
||
> live VM's own `vm_heartbeat_run_cycle()` (`vm_runtime.c:502`), incrementing one shared
|
||
> global `fleet_heartbeat_tick_count`, and fires `vm_physics_tick()` once that shared
|
||
> counter — fed by all VMs at once — reaches `HEARTBEAT_INFERENCE_FREQUENCY` (1000). A
|
||
> lone VM needs 1000 of *its own* 256-execution cycles to trip its own inference gate;
|
||
> with `STADIUM_MAX_VM_COUNT`'s real default of 4 (Hera + 2×Hermes + Artemis, Tripod's
|
||
> actual topology), the shared fleet counter can reach the same threshold up to ~4×
|
||
> sooner in wall-clock terms. So in a live multi-VM fleet, capacity arbitration can run
|
||
> *more* often in real time than any individual VM's own heat-inference loop, not less —
|
||
> exactly the ordering §22.4 calls "required, not preferred" because getting it backwards
|
||
> "produces a system that thrashes while every individual rule looks correct."
|
||
> 2. **The fix §22.4 already specified was never actually built.** §22.4's own resolution
|
||
> text says the capacity-tick should get "its own named constant rather than literally
|
||
> sharing `HEARTBEAT_INFERENCE_FREQUENCY`... named and made a Kconfig symbol at
|
||
> implementation time (item 3.1)." `STADIUM_CAPACITY_TICK` exists in `Kconfig.kernel`
|
||
> exactly as promised, default 1000 matching the cited precedent — but repo-wide grep
|
||
> confirms it is never read by any code path. Its only other appearance is a
|
||
> compile-time static assertion in `include/starkernel/vm/stadium.h:118` checking it's
|
||
> `> 0`, which gates nothing at runtime. `vm_physics_heartbeat_tick()` still literally
|
||
> shares `HEARTBEAT_INFERENCE_FREQUENCY`, i.e. the code is still in the
|
||
> *pre-implementation* state §22.4 described as its starting precedent, not the resolved
|
||
> design it specified.
|
||
>
|
||
> Also found in passing, minor and not a correctness issue: `vm_tick()`'s own header
|
||
> comment (`vm_runtime.c:104`, "Aggregates all periodic optimization tasks (Loop #3 and
|
||
> Loop #5) into one place") is stale — Loop #3 is not called inside `vm_tick()`, it's
|
||
> called separately from `vm_heartbeat_run_cycle()` on the faster 256-execution cadence,
|
||
> not the 256,000-execution one `vm_tick()` itself gates.
|
||
>
|
||
> Where this leaves Q5: within a single cadence tier (the 256-execution cluster, or the
|
||
> 256,000-execution cluster), several nominally-distinct loops share one gate with zero
|
||
> separation between them — apparently intentional unification (Phase 2's "Unified
|
||
> Inference Engine" merged #5/#6, L8 explicitly reads #1–#7's latest outputs each vote),
|
||
> not obviously a bug. *Between* tiers, separation is comfortably past the order-of-magnitude
|
||
> minimum (256:1 and 1000:1). The one place separation is required by name and is not
|
||
> actually present is the heat/capacity pair finding #1 above — that's the concrete,
|
||
> verified answer to "the risk is interference." **Left open, not fixed**, per this
|
||
> document's discipline — whether to wire `STADIUM_CAPACITY_TICK` in for real (giving Hera
|
||
> her own slower cadence independent of per-VM tick counts) or something else is Captain
|
||
> Bob's call, not made here.
|
||
- [ ] **§17.4 — the framebuffer utility's internal heat/decay dynamics are undesigned.**
|
||
Explicitly "Open, deferred": not a Stadium patron, but what physics (if any) governs it
|
||
internally was never designed. Not blocking anything.
|
||
|
||
> **CHECKED 2026-08-13, still correctly not ripe — precondition refined, item left open.**
|
||
> The actual FABRIC.md text (end of §17.5, not really §17.4 — that section number is a
|
||
> loose reference to the same "OPEN, deferred" paragraph) is explicit: "What those
|
||
> dynamics are is a question for when the framebuffer work actually happens... it should
|
||
> not be designed speculatively now." The console/framebuffer work (item 4.3/4.4 series)
|
||
> has since substantially shipped and is live, which looks at first glance like that
|
||
> precondition is now met. It isn't: grepped `src/starkernel/hal/framebuffer.c`,
|
||
> `vt100.c`, `console.c` (1,800 lines total) for `heat`/`decay`/`dirty` — zero hits. Every
|
||
> write is immediate-mode (`fb_scroll_rows()`, `fb_scroll_rect()`, direct glyph blits at
|
||
> the point of writing); there is no damage list, no dirty-region tracking, nothing a
|
||
> per-region heat/decay scheme would even attach to yet. That's consistent with Section
|
||
> A's item 1.11 (dirty-event granularity) still being unstarted — this item's true
|
||
> prerequisite is 1.11, not "the framebuffer work" in general as originally framed here.
|
||
> Designing framebuffer-internal physics before the dirty-event mechanism they'd govern
|
||
> exists would be exactly the speculative work FABRIC.md warns against. Left open, framing
|
||
> corrected: blocked on item 1.11, not merely deferred.
|
||
- [x] **§23.4 #2 — cell-size validation against a real message shape.** "Check that a typical
|
||
message still fits in one cell" was never done — item 3.1's own done-note confirmed no
|
||
message-patron struct existed yet at the time. This is now checkable: item 4.2 built
|
||
Hermes's actual message/channel structures on the Stadium, so the 32-byte inline payload
|
||
can finally be validated against something real instead of a hypothetical.
|
||
|
||
> **SETTLED 2026-08-13, but not as originally framed.** Read Hermes v1's real message
|
||
> struct (`capsules/hermes/init.4th`, blocks 4100/4105/4143, item 4.2): `MSG-CELLS` is 9
|
||
> cells — `TYPE, FROM, TO, PADDR, PLEN, STADIUM-CELL, SEQ, CH, ORIG-TYPE` — at 8 bytes/cell
|
||
> (`cell_t` is `int64_t`/`long`, `include/vm.h:71-75`), i.e. **72 bytes**, already over
|
||
> §23.3's whole proposed 64-byte cell before any payload. And the payload itself is
|
||
> **not inline at all**: `MSG-SEND` (`:208-213`) takes a caller-supplied `paddr plen` —
|
||
> wherever the caller already put the bytes (a string literal via `S"`, a block buffer) —
|
||
> and stores only the pointer and length in the struct. There is no fixed-size inline
|
||
> payload field to check a message against. So §23.4 #2's literal question ("does a
|
||
> typical message fit in the 32-byte inline payload") is **moot, not answered**: the
|
||
> shipped design didn't adopt §23.3's speculative 64-byte-cell/32-byte-inline-payload
|
||
> scheme for messages at all, it used a larger fixed control block plus out-of-line
|
||
> pointer/length indirection instead. Closing as settled-by-divergence — the real
|
||
> implementation went a different direction than the design question assumed, which is
|
||
> itself the answer; §23.3's cell-size numbers were never made load-bearing for Hermes
|
||
> messages and nothing here depends on reconciling them.
|
||
|
||
---
|
||
|
||
## E. Documentation debt
|
||
|
||
- [x] **Taxonomy / glossary.** Captain Bob's own flag, 2026-08-04: the physics vocabulary
|
||
(heat, mass, density, patron, Stadium, and Kconfig knob names built on it) needs an
|
||
explicit glossary stating these are named analogies, so code, Kconfig, and documentation
|
||
terminology don't drift apart over time. Never scoped into any phase. Real, acknowledged
|
||
debt: "I guess that we didn't finish out FABRIC.md quite as much as we thought."
|
||
|
||
> **DONE 2026-08-13.** `ONTOLOGY.md` already existed and already did exactly this job for
|
||
> the original heat/decay/inference vocabulary (Section III's lexicon, Section 3.2's
|
||
> "avoid/deprecated terms" table) — it just predated the Stadium work entirely (confirmed:
|
||
> zero mentions of "Stadium," "patron," "mass," or "density" before this edit) and was
|
||
> never extended to cover it. Added new §IX "Stadium/Fabric Vocabulary" in the same
|
||
> format as the existing lexicon: a metaphor-mapping table, alphabetical definitions for
|
||
> Cell, Code field, Density, K, Mass, Mass-vs-heat-conservation, Patron, Reap, Stadium,
|
||
> Warehouse, and Utility (all cited back to their `FABRIC.md` DECIDED sections, not
|
||
> invented), plus a Kconfig-knob-to-concept table with **verified, not assumed** wiring
|
||
> status for every `STADIUM_*` symbol — including flagging `STADIUM_CAPACITY_TICK` as
|
||
> dead, the same finding this document's own §12 Q5 closure made independently, now
|
||
> cross-referenced from the glossary too. Bumped `ONTOLOGY.md` to v1.1 with a version-
|
||
> history entry. No FABRIC.md/FABRIC-2.md content moved — this is a new, additive section
|
||
> in the document that already owned this job.
|
||
- [ ] **ACL-RWT DoE overhead re-measurement.** The measured overhead numbers in
|
||
`.claude/CLAUDE.md` ("+0.0054%–+0.0088%") were all captured at `-O0`, before item 4.5
|
||
enabled real compiler optimization. Nobody has re-measured, or even confirmed the old and
|
||
new numbers are comparable at all. Flagged in passing during item 4.5f, never formally
|
||
scoped.
|