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>
8.3 KiB
Punch List: Gaps from 2026-07-24 Session
Carried over to fix in a fresh session. Written at end-of-session while
wrapping up — see CHANGELOG.md on both branches for what was
accomplished today; this file is only the leftover gaps.
1. Port master's riscv64 bug fixes to lithosananke (highest priority)
Getting the hosted riscv64 acceptance test to build under clang on master
surfaced several real bugs. They were fixed on master only. This broke
an invariant established at the start of today's session: we verified via
SHA-256 that the ASM-optimization headers (including
include/vm_asm_opt_riscv64.h) were byte-identical between master and
lithosananke. That's no longer true.
Most important: include/vm_asm_opt_riscv64.h's vm_pop_asm/vm_rpop_asm
had a genuine SIGSEGV-causing register-reuse hazard (the inline asm
referenced the dsp/rsp memory operand both before and after writing the
output value operand, with nothing pinning the address register in between
— clang's allocator picked the same register for both and corrupted the
write-back; GCC happened to pick different registers and never hit it).
Fixed on master (commit 4db9946a) by writing dsp/rsp back before
loading the value. This bug is still live and unpatched in
lithosananke's copy of the same file — latent today only because
lithosananke's kernel build currently uses GCC, but it's a real crash
risk if that ever changes (different GCC version, different -O level, or
a future switch to clang).
Also unported to lithosananke, all from master commit 4db9946a:
src/inference_engine.c—q48_from_u64(0.8)→q48_from_double(0.8)(was silently truncating to 0)src/math_portable.c—-100LL << 16(UB, shifting a negative value) →-(100LL << 16)src/block_subsystem.c— removed deadlbn_to_pbn()(unused function, clang caught it)src/physics_pipelining_metrics.c— removed deadq48_mul_q48()src/word_source/editor_words.c— removed deadset_scr()src/word_source/starforth_words.c— removed deadreset_countlocal
Action: cherry-pick or manually port these onto lithosananke, then
re-verify the standing kernel 3-arch QEMU acceptance test still passes
(dead-code removals and the two arithmetic fixes are behaviorally inert for
the kernel path except where they touch real bugs; the asm fix is the one
that actually matters).
2. Fix the broken riscv64 command in this branch's acceptance-test doc
docs/lithosananke/hosted-acceptance-test/README.md still documents the
riscv64 leg using riscv64-linux-gnu-gcc, which cannot build this tree
(nanosleep visibility failure under -std=c99, unrelated to any project
change). This was already fixed in master's .claude/CLAUDE.md (the
working command is clang-18 --target=riscv64-linux-gnu --sysroot=/usr/riscv64-linux-gnu with -std=c11 -pthread and
-fuse-ld=lld) but the correction was never carried over to this doc.
Action: copy the corrected command block from master's
.claude/CLAUDE.md (### "Run the acceptance test" section) into this
doc.
3. riscv64 hosted build isn't reachable via plain make
There's no Makefile target that builds riscv64 hosted with clang — the only
working invocation is the long manual command living in CLAUDE.md and
commit messages. A plain make ARCH=riscv64 still fails with GCC.
Action (lower priority, needs a decision, not just a fix): decide
whether to (a) add a riscv64-clang (or similar) Makefile target that
wires in the working toolchain automatically, (b) fix GCC riscv64
compatibility directly (would need to chase the nanosleep
visibility issue at its root — not yet diagnosed beyond "gcc riscv64 fails,
clang succeeds with different flags"), or (c) leave it manual and just keep
the docs accurate. Not urgent since the documented manual path works.
4. amd64 kernel dict_hash non-determinism — root cause is a hypothesis, not proven
Today's kernel acceptance re-run found amd64's dict_hash deviated from
aarch64/riscv64 (which matched each other and the established reference
exactly). Traced to capsule_dict_hash_hook() folding execution_heat
into the hash, combined with amd64's boot log stating its timer runs in
"RELATIVE mode (no determinism guarantees)" under this QEMU/hypervisor
setup (no invariant TSC) — unlike aarch64/riscv64, which get real hardware
timers. Plausible and consistent with existing ACL-RWT campaign data
showing amd64's tick count already diverging from the other two
architectures, but this was a single observation, not confirmed by
repeated runs.
Action: re-run the amd64 kernel leg 3-5 times back to back and confirm
dict_hash actually varies each time (proving jitter) rather than being
stable-but-different (which would point to something else). If confirmed,
consider whether CHANGELOG.md/CLAUDE.md's acceptance criteria need a
one-line amendment: "identical dict_hash across all three architectures"
should probably be scoped to aarch64/riscv64, with amd64 checked only for
clean boot + PARITY:OK + E2E PASS.
CLOSED (2026-08-02): does not reproduce. Ran the amd64 kernel leg 5
times back to back (logs/20260802-074443, -074813, -075033,
-075254, -075522, plus the same day's earlier acceptance run at
-064424 — 5 runs total). dict_hash was byte-identical across every
run for every VM (Artemis 0x7b29c1e7d6714051, both Hermes instances
0x2e8c1b07d009507e, Hera/MAMA_INIT 0x450998b22c298cd9), and matched
aarch64/riscv64 exactly in every case. Not jitter, and not
stable-but-different either — just correct, every time.
The hypothesis's premise still holds — the amd64 boot log still warns
invariant TSC not present under hypervisor... continuing in RELATIVE mode (no determinism guarantees), and capsule_dict_hash_hook()
(src/starkernel/capsule/capsule_vm_hooks.c:65-66) still folds
execution_heat into the hash, unchanged since this list was written.
But PARITY:MAMA_INIT (and the child-VM PARITY:BIRTH lines) print
before Starting heartbeat... in the boot sequence, and heat only
decays on heartbeat ticks — so there is no window for wall-clock jitter
to touch execution_heat before any of these hashes get computed. The
mechanism is real; it just isn't reachable at this checkpoint. Most
likely the original observation was a one-off (loaded host machine,
coincidental timing) rather than a real gap.
No code change made. No amendment to CLAUDE.md's acceptance criteria —
"identical dict_hash across all three architectures" holds up under
repeated testing, not just the single run that first raised this.
5. lfs/riscv64/starforth isn't bit-for-bit what make fastest would produce
The committed riscv64 LFS binary (on master) was built with my manual
clang invocation, which omitted several of TARGET_CFLAGS_fastest's flags
(-funroll-loops -finline-functions -fomit-frame-pointer -fno-plt -fno-semantic-interposition) and used -fuse-ld=lld instead of the
default linker. Functionally fine (acceptance test passed), but worth
knowing it's not exactly reproducible via the standard make fastest
recipe. Resolves naturally once item #3 is decided.
6. Repo size growth from committed QEMU acceptance-run artifacts
Not a bug, just a trend worth being aware of: today's two kernel acceptance
runs on this branch alone added 1M+ lines of committed log/DoE-CSV data,
including one 82MB serial log GitHub explicitly warned about
(logs/20260724-191335/amd64/qemu-amd64-20260724-191335.log). Matches
existing project convention (every real acceptance run gets committed), but
if this keeps compounding it may eventually be worth a retention/pruning
policy for logs/ and experiments/bare_metal/runs/. No action needed
now — just flagging.
Not gaps, just context for next time
- The 2 pre-existing
SPAN.zero_input/SPAN.variable_testtest failures (727/800 baseline on all three hosted architectures, observed onmaster) predate today's session — confirmed present before any of today's fixes were applied. Not investigated further today; still an open item if anyone wants to chase it down. - Today's aarch64/riscv64
.hiddenISR hardening (commit5fe188bd) only has a CHANGELOG entry, not a dedicated writeup the waydocs/lithosananke/amd64-isr-fix/README.mddocuments the original amd64 fix. Not urgent — the CHANGELOG entry covers it — but noting the asymmetry in case someone later wants doc parity.