feat(log): LOG-FLUSH -- a VM's log records reach Artemis's log ring; and the ring keeps its order when it grows

FABRIC-3 SXXXII.4's deferred LOG-FLUSH, built to the 2026-10-14 rulings
(SXXXVIII): everything a VM logs, at or above the log level, goes into its
own buffer; it goes to Artemis on LOG-FLUSH, when full, and at once on an
ERROR, as one kernel-Hermes LOG_BATCH message she writes on her own turn,
the source being the sender the kernel stamped.  Records that cannot go yet
are counted and reported with the next flush.  LOG-SHOW (Hera only) reads
the ring back -- it had no reader.

Found by the first test that filled it: ring growth overwrote the oldest
record and put blank slots inside the live range.  Fixed test first.

Three ISAs: POST 1052 passed; new parity baseline word_count=526
hash=0x82f4ae7cb2e2d424 (deliberate).  CLAUDE.md: Phase 8 PKI closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
rajames
2026-10-14 12:10:01 -04:00
co-authored by Claude Opus 5.5
parent 5e26fad30d
commit 4da370f492
35 changed files with 91102 additions and 15 deletions
+44 -1
View File
@@ -4602,7 +4602,7 @@ call sites (`repl.c` has 18); most are direct interactive replies already correc
this rule -- reclassifying the specific sites that aren't is Stage D's job (the same files Stage
C's triage already walks), not repeated here.
**Decision on the two §XXVII gaps: fix eviction now, defer `LOG-FLUSH` again.**
**Decision on the two §XXVII gaps: fix eviction now, defer `LOG-FLUSH` again.** *`LOG-FLUSH` built 2026-10-14 -- §XXXVIII.*
`LOG-FLUSH` needs real new machinery first -- the per-VM log buffer its own doc comment already
presumes (`vm_log_buffer.h`) does not exist anywhere in the tree, confirmed by grep; building it
means designing that buffer format and the flush word together, genuine feature work deserving
@@ -5633,3 +5633,46 @@ kernel's freestanding `snprintf` does not do.
**Found, not fixed (pre-existing, unrelated):** Hera prints two and Hestia three `( Unterminated
comment` warnings at every boot, in logs from before this work too.
## XXXVIII. `LOG-FLUSH` built: a VM's log records reach Artemis's log ring (2026-10-14)
Deferred in §XXXII.4 as its own initiative; Captain Bob, 2026-10-14: build it before the
bare-metal builds. His rulings: **everything the VM logs** goes in its buffer (at or above the
log level -- the one, global `LOG-LEVEL!` setting, the same filter `log_message()` applies);
it goes to disk **on `LOG-FLUSH`, when the buffer fills, and at once on an ERROR record**.
**Built.**
- Each VM has a buffer of its own (`VM.log_buf`, 900 bytes, packed records: level, timestamp,
length, text; `vm_log_buffer.c`/`.h`). `log_message()` (`shim.c`) adds every record it prints
to the buffer of the VM it is attributed to (`vm_log_attributed_vm()`); a record made outside
any VM's text -- the kernel's own -- belongs to none and is not kept.
- A flush is one kernel-Hermes message to Artemis, type `SK_HERMES_MSG_TYPE_LOG_BATCH` (10), the
one message type that is not FORTH text: `sk_hermes_drain_checkpoint()` hands it to
`vm_log_batch_deliver()`, which writes each record with `log_region_append()` -- in Artemis's
own context, on her turn (§XXXVI). The record's source is the sender the kernel stamped on the
message, never a name the sender gives: a VM cannot write under another's name.
- Nothing is lost silently: records that cannot go (Artemis not yet live -- all of Hera's POST --
or the send refused) are counted, and the next flush that can be sent leads with "N log
records not sent: Artemis not ready" (175 at a normal boot).
- `LOG-FLUSH ( -- )`, every VM. `LOG-SHOW ( n -- )`, Hera only: the newest n records, oldest of
them first, as source, level, text -- added because the ring had no reader at all, so nothing
written there could be read or checked (`log_region_read_recent()`). Hera only because it shows
every VM's records, and Hera's console is Zuse's.
**A real defect in the ring, found by the first test that filled it:** growth
(`log_region_append()`) claimed to be "safe regardless of wrap state" and was not. The ring is
full when it grows, so head == tail; nothing moved and tail stayed put, so the next write
overwrote the oldest record and the new, blank slots read back as records inside the live
range. 30 records written to a fresh ring read back as 28 of them and 12 blank
(`logs/20261014-115648`; also `-115434` on a ring already damaged by an earlier test). Fixed:
the records from head on move up by the slots added, and the next write goes into the gap (with
head at 0 -- the only case growth meets, since nothing is evicted before the ring is at its full
size -- simply at the old end). §XXXII.4 had flagged eviction as never live-exercised.
**Verified.** Test first: `LOG-FLUSH` and `LOG-SHOW` unknown (`logs/20261014-114028`). Then:
Hera's WARN probe and her boot warnings, under source `Hera`, after `LOG-FLUSH`; identity `00`'s
ERROR probe, sent by itself at once, under source `00` (`-114847`); the lost-records notice
(`-115434`); 30 records through a growth, in order, no blank, on amd64 (`-115841`), aarch64
(`-120526`) and riscv64 (`-120746`). Standard acceptance, all three ISAs: `zuse@Hera`, POST 1052
passed, no unknown word, **new baseline `word_count=526 ... hash=0x82f4ae7cb2e2d424`**
(deliberate: Hera's new words) (`logs/20261014-120027`, `-120148`, `-120356`).