Ran the amd64 kernel acceptance leg 5 times back to back as the punch
list's action item asked. dict_hash was byte-identical across every run
for every VM (Artemis, both Hermes instances, Hera/MAMA_INIT), and
matched aarch64/riscv64 exactly each time. Not jitter, and not
stable-but-different either.
The underlying mechanism the hypothesis pointed at is still real and
unchanged (capsule_dict_hash_hook() still folds execution_heat into the
hash; amd64 still runs its timer in RELATIVE mode under this
hypervisor) — but PARITY:MAMA_INIT and the child-VM PARITY:BIRTH lines
all print before the heartbeat starts, and heat only decays on
heartbeat ticks, so there's no window for the timer's non-determinism to
reach execution_heat before any of these hashes get computed. Most
likely the original 2026-07-24 observation was a one-off (loaded host
machine, coincidental timing), not a real gap.
No code change. No amendment to CLAUDE.md's acceptance criteria needed —
"identical dict_hash across all three architectures" holds up under
repeated testing. Closure note added to the punch list; the 5 verification
runs' logs and DoE CSVs are kept as the supporting evidence.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Resolves item #3 of docs/working/archive/session-logs/2026-07-24-punch-list.md
("riscv64 hosted build isn't reachable via plain make"), the last open
item from that list. Decided against options (b) chasing GCC's riscv64
nanosleep-visibility failure at its root (undiagnosed, open-ended) and
(c) leaving it manual — instead wired the already-verified clang recipe
(commit 4485c38 / e287334) into the Makefile, mirroring the existing
rpi4-cross pattern.
Makefile: new riscv64-clang target. CFLAGS deliberately does not reuse
$(BASE_CFLAGS) (hardcodes -std=c99); clang needs -std=c11 -pthread here
instead. Registered in `make help` and .PHONY.
docs/lithosananke/hosted-acceptance-test/README.md: riscv64 section now
points at `make riscv64-clang` instead of the long manual invocation.
Updated Background section and commit list to reflect that all three
punch-list items touching this doc (#1 asm fix, #2 doc command, #3 make
target) are now resolved.
Verified: `make riscv64-clang` produces a binary with identical results
to the manual command it replaces (965 passed / 0 failed, "ALL
IMPLEMENTED TESTS PASSED!", "3 Goodbye!" for the piped acceptance script).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Completes the highest-priority item from
docs/working/archive/session-logs/2026-07-24-punch-list.md (item #1),
intentionally deferred out of commit 4485c38 as a separate, more careful
change.
vm_pop_asm/vm_rpop_asm's inline asm referenced the dsp/rsp memory operand
twice (read near the top, write-back near the bottom) while also writing
a plain register output operand (%[val]) in between. Nothing pinned the
address register computed for the memory operand across that gap, so
clang's allocator could reuse it for %[val], corrupting the write-back.
GCC happened to pick different registers and never hit it — this repo's
kernel build uses GCC and USE_ASM_OPT is never defined there, so the bug
was latent, not live, prior to this fix.
Fixed by reordering: write dsp/rsp back before loading the popped value,
so the memory operand's final use has already happened by the time the
output register is live. Same fix as the old pre-split monorepo's master
commit 4db9946a, re-derived here since that commit lives in a different
repository post-split.
Verified empirically, not just theoretically: rebuilding the riscv64
hosted binary with the documented clang -O3 -DUSE_ASM_OPT=1 acceptance
recipe went from 955 passed / 10 failed (all CASE.* control-flow tests —
exactly what stack-pop corruption would hit) to 965 passed / 0 failed,
"ALL IMPLEMENTED TESTS PASSED!", with nothing else changed. All three
Makefile.starkernel kernel builds still compile clean; the change is
inert there since USE_ASM_OPT is never defined for the kernel build.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Addresses items #1 (partial) and #2 of
docs/working/archive/session-logs/2026-07-24-punch-list.md.
docs/lithosananke/hosted-acceptance-test/README.md:
- riscv64 leg used riscv64-linux-gnu-gcc, which fails to build this tree
(nanosleep visibility under -std=c99). Replaced with the working
clang-18 --target=riscv64-linux-gnu --sysroot=/usr/riscv64-linux-gnu
invocation, verified end-to-end.
- All three arch sections referenced a -c "<script>" flag that has never
existed in cli.c/main.c. Corrected to the working
`echo "..." | starforth -s` pattern, verified on all three architectures.
- Updated Prerequisites: qemu-user alone is sufficient (guest binaries are
static; qemu-user-static provides static *emulators*, not required here).
Source fixes (ported from the old pre-split monorepo's master, commit
4db9946a, where they were made but never carried over to this line):
- src/math_portable.c: `-100LL << 16` is UB (shifting a negative value)
under clang's -Wshift-negative-value; changed to `-(100LL << 16)`.
- src/physics_pipelining_metrics.c: removed dead q48_mul_q48()
(-Wunused-function under clang; GCC doesn't flag this by default).
- src/word_source/editor_words.c: removed dead set_scr() (same reason).
These three were required just to get the documented clang build to
compile at all. The punch list's higher-severity item — a genuine
SIGSEGV-causing register-reuse hazard in vm_pop_asm/vm_rpop_asm
(include/vm_asm_opt_riscv64.h) — is intentionally NOT included here; it's
a separate, more careful change and isn't required for this build to
succeed (latent only under clang; this repo's kernel build uses GCC).
Verified: all three hosted builds compile and run correctly (amd64
native, aarch64 via qemu-aarch64, riscv64 via qemu-riscv64), each
printing "3 Goodbye!" for the piped `1 2 + . BYE` script. All three
Makefile.starkernel builds (amd64/aarch64/riscv64) still compile cleanly
with these shared vendored-source changes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Write-up of the 2026-08-02 riscv64 boot crash investigation and fix
(commit 7366275), in the same style as docs/lithosananke/amd64-isr-fix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
kernel_main on riscv64 ran directly on EDK2's UEFI boot-time stack, with
no dedicated stack switch — amd64 has always had a kernel_entry.S
trampoline for exactly this reason (its own comment: "the FORTH
interpreter + DOE experiment loop can easily exceed that depth").
aarch64 happens to get away without one because its firmware's default
stack is apparently larger, but that was never a guarantee.
On riscv64 the VM bootstrap's call depth (27 word-registration modules
-> physics/SSM init -> Tripod capsule birth) overflowed that small
stack, corrupting a return address and producing a wild jump / page
fault right after vm_init_with_host() returned — reproduced consistently
across the 2026-08-01 DoE campaign logs.
- src/starkernel/arch/riscv64/kernel_entry.S (new): RISC-V stack-switch
trampoline mirroring amd64's, giving the kernel a dedicated 2 MiB BSS
stack before anything deep runs.
- kernel_main.c: riscv64 now builds kernel_main_impl (invoked via the
trampoline) instead of kernel_main directly, same pattern as amd64.
- Makefile.starkernel: wires the new file into the riscv64 build.
- uefi_loader.c: RAW_LOG() was silently a no-op on every non-amd64 arch;
added a real raw-UART writer for riscv64 (QEMU virt's uart8250 at MMIO
0x10000000) so existing loader diagnostics actually produce output.
Verified: all three architectures boot clean to [Hera] ok> in the
required order (amd64, aarch64, riscv64); logs and DoE CSVs from these
runs included.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The aarch64 loader link step hardcoded the unversioned "lld-link", which
isn't on PATH by default on this Debian/Ubuntu setup (the package only
installs lld-link-18 under /usr/bin; unversioned lld-link lives under
/usr/lib/llvm-18/bin). CI worked around this with an explicit PATH prefix
in the workflow; a local build without that PATH override failed. Now
auto-detects whichever name resolves, falling back to the versioned name.
Verified: aarch64 builds clean with the default PATH.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Kernel version only — the embedded StarForth engine version (3.1.0) is left
alone since it tracks a vendored copy that has genuinely diverged from the
standalone StarForth repo, not something to auto-sync. Verified builds on
amd64, aarch64, and riscv64.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
LithosAnanke is now its own repository rather than a branch inside the
combined StarForth/LithosAnanke monorepo, so this drops the old
master(StarForth)/lithosananke(kernel) branch-topology framing in favor of
the current reality: this repo's master is the sole LithosAnanke production
line. Ground-truths numbers that had drifted (kernel tree file count,
word_source file count, capsule count), reframes the vendored VM source as
the embedded engine it actually is rather than a second production target,
and independently verifies the ACL kernel-parity and DoE-campaign claims
against the actual current code. Fixes README's self-referential
"see master branch" link to point at the separate StarForth repo instead.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>