Files
LithosAnanake/docs/working/archive/session-logs/2026-07-24-punch-list.md
T
Robert Allan JamesandClaude Sonnet 5 1e2dc7fbf0 Close punch-list item #4: amd64 dict_hash non-determinism does not reproduce
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>
2026-08-02 08:05:28 -04:00

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.cq48_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 dead lbn_to_pbn() (unused function, clang caught it)
  • src/physics_pipelining_metrics.c — removed dead q48_mul_q48()
  • src/word_source/editor_words.c — removed dead set_scr()
  • src/word_source/starforth_words.c — removed dead reset_count local

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_test test failures (727/800 baseline on all three hosted architectures, observed on master) 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 .hidden ISR hardening (commit 5fe188bd) only has a CHANGELOG entry, not a dedicated writeup the way docs/lithosananke/amd64-isr-fix/README.md documents the original amd64 fix. Not urgent — the CHANGELOG entry covers it — but noting the asymmetry in case someone later wants doc parity.