Commit Graph
100 Commits
Author SHA1 Message Date
Robert Allan JamesandClaude Sonnet 5 dbaead0af2 xhci: route driver chatter through log_message(), silence at default log level
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
xhci.c and repl.c's USB attach/detach path printed every xHCI command
submission, completion, and BOT transfer step unconditionally via
console_println()/console_puts() -- floods the serial log on every boot,
regardless of whether anyone is debugging the USB stack.

Converted every "xhci:"-prefixed line to log_message() with a level
chosen by what it reports, not blanket debug:
- LOG_ERROR: allocation/mapping failures, timeouts, command failures,
  CSW signature/tag mismatches, CSW FAILED/PHASE ERROR, "not implemented"
  refusals, every "deferred ... setup failed" path
- LOG_WARN: dropped/skipped conditions (command ring busy, tracked-port
  range exceeded), unrecognized media (bad version/CRC), TUR retry
- LOG_DEBUG: routine progress (command submitted, succeeded, transfer
  completed, port connected) and expected outcomes (recognized/blank
  media)

Default log level is LOG_INFO, so the LOG_DEBUG chatter that was the
actual complaint is now silent by default and re-enabled with
--log-level=debug; LOG_ERROR/LOG_WARN stay visible so real faults aren't
buried.

xhci_log_hex32() now routes through log_message(LOG_DEBUG, ...) instead
of console_puts()/console_println() directly -- kept its own zero-padded
8-digit hex formatting rather than switching to log_message()'s %x
(which has no width control), since register values lining up in the
log is the reason this helper exists. console.h dropped from xhci.c,
no longer used directly.

Verified functionally unchanged, not just "still boots": all three
architectures reach zuse)ok>, log_message()-instrumented lines are gone
from the default-level log (grep -c xhci == 0 on all three, versus dozens
before), and the USB thumbdrive path still works end to end --
"Zuse: identity confirmed from attached thumbdrive" appears on all three
boots exactly as before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 01:25:50 -04:00
Robert Allan JamesandClaude Sonnet 5 1bd041e84d FABRIC-3.md §IV.3 item 5: pci_init() DTB path re-scoped, doc-only, nothing implementable this pass
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Re-examined the Pi 5 RP1 PCIe access re-scoping (§IV.3 item 5) to see what
was actually buildable before real hardware arrives (2026-09-17), and
found nothing safely implementable this pass -- recorded three corrections:

1. The "vmm_map_range() needed for native boot" secondary finding is
   wrong as stated: arch_mmu_init() is a stub on both non-amd64 ISAs,
   vmm.c's load_cr3() is a no-op outside __x86_64__, and riscv64's satp
   is explicitly left at Bare (this session's own §V.3 item 7 fix). No
   non-amd64 ISA ever turns its own MMU on today, so VA==PA and there is
   nothing to map. Reframed as a future concern conditional on MMU
   bring-up, not a current prerequisite.

2. No silent-wrong-answer risk exists today: with no ACPI on native Pi 5
   boot, pci_init()'s MCFG parse already fails cleanly and aarch64 takes
   no ECAM fallback, rather than misreading RP1's 37 KB indirect window
   as flat ECAM.

3. The "make config-space dispatch runtime instead of compile-time" half
   of the re-scoped fix, considered on its own, isn't separable:
   portio_read32() and siblings use outl/inl inline asm that cannot exist
   in an aarch64/riscv64 translation unit, so any case referencing them
   stays #ifdef-gated regardless -- an enum wearing the same compile-time
   selection. It only becomes real dispatch once a second non-amd64
   mechanism is actually compiled in.

Broadcom indirect access itself remains unimplemented: no QEMU model
exists for "brcm,bcm2712-pcie" (zero branches would ever execute before
real hardware, unlike the guard-exercised GIC/PLIC DTB fixes), and
config-space access alone is insufficient for RP1 to enumerate without
the real driver's controller bring-up (link training, PERST, window
setup) -- building only the index/data window would compile and boot
while silently never working.

No code changed. 3-arch acceptance run anyway per convention: all three
reach zuse)ok> with identical virtio-blk/xHCI PCI device discovery to
the pre-change baseline (commit 9b6de5d), confirming no regression from
a change that touched no source.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 01:06:24 -04:00
Robert Allan JamesandClaude Sonnet 5 9b6de5d6c7 riscv64: PLIC base address DTB-discovered, QEMU-virt constant as fallback (§V.3 item 3)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
plic_init() now takes boot_info->dtb (threaded through apic_init()) and
tries fdt_find_node_by_compatible(dtb, "sifive,plic-1.0.0") ->
fdt_find_prop_in_node(..., "reg", ...) before falling back to the
QEMU-virt-specific constant it previously hardcoded unconditionally.
Reuses the node-scoped DTB lookup primitive built for the aarch64 GIC
base fix unchanged. s_plic_base is now a runtime uintptr_t, same shape
as apic.c's s_gicd_base/s_gicc_base.

This system's QEMU/UEFI riscv64 firmware does not forward a DTB to the
guest (timer.c's own timebase-frequency read falls back too, confirmed
in this boot's own log), so only the no-DTB fallback branch is exercised
here -- the success branch (a real DTB with a matching PLIC node) stays
unverified until real Milk-V Mars hardware. FABRIC-3.md's first-drafted
claim that the success branch would run (based on a stale comment in
plic.c's own pre-fix header) was checked against the actual log and
corrected before this commit.

3-arch acceptance: amd64/aarch64 don't compile these files, so their
runs are non-regression on untouched files only. riscv64's own boot log
confirms the fallback path prints exactly as designed and boot reaches
zuse)ok> unchanged.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 00:56:25 -04:00
Robert Allan JamesandClaude Sonnet 5 90ee8deb6d riscv64: skip satp Bare-mode switch when already Bare (§V.3 item 7 audit fix)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
arch_early_init() unconditionally cleared satp on every boot, justified
only by behavior observed under QEMU/EDK2 firmware (satp.MODE=10/Sv57,
kernel identity-mapped within it). That reasoning never applied to the
native U-Boot+OpenSBI boot path, where satp is conventionally already 0
at S-mode handoff -- the unconditional clear was likely a harmless no-op
there, but on an unverified assumption.

Fix: read satp.MODE first and return early when it's already 0 (nothing
to switch away from, no safety argument needed). The unconditional
csrw/sfence pair still runs unchanged for the confirmed QEMU/EDK2 case.
No Sv39/Sv48/Sv57 page-table walker built -- out of proportion to this
finding's severity.

3-arch acceptance: amd64/aarch64 don't compile this file, so their runs
are non-regression on untouched files only. riscv64's own boot log
confirms satp.MODE = 0xa at entry, so the mode != 0 branch ran and
"satp cleared -- Bare mode, explicit" printed exactly as before the fix.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 00:17:35 -04:00
Robert Allan JamesandClaude Sonnet 5 350287850f arch/aarch64: fix VBAR_EL2 stale comment + hardcode HVC PSCI conduit
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
FABRIC-3.md §IV.3 item 7's code audit found two more findings; fixed both
per direct instruction naming them specifically.

Comments only, no behavior change:
interrupts.c's arch_interrupts_init() doc comment (and its matching
runtime EL2 console message) claimed VBAR_EL2 was "a known gap... not yet
wired." Checked against isr.S's aarch64_install_vectors() and found that
claim stale: the assembly already branches on aarch64_current_el() and
writes vbar_el2/vbar_el1 correctly, threading the same answer through
el2_mode_flag for the IRQ trampoline's ELR/SPSR selection too. There was
no gap to close -- only the comment was wrong. Also fixed a same-vintage
one-word staleness in arch.c's aarch64_install_vectors extern comment
("installs VBAR_EL1" -> EL-aware), caught while touching this.

Real behavior change, gated to preserve existing QEMU behavior:
arch_cold_reset() hardcoded the PSCI conduit to HVC, a documented
QEMU-specific workaround (QEMU's AAVMF has no genuine EL3 to answer SMC).
Real Pi 5 hardware's ATF means EL3 exists there; confirmed further this
pass that bcm2712.dtsi's own /psci node declares method="smc" directly,
not just inferred from ATF's presence.

New aarch64_psci_conduit_init(dtb) in arch.c: fdt_valid(dtb) ->
fdt_find_node_by_compatible(dtb, "arm,psci-1.0") ->
fdt_find_prop_in_node(..., "method", ...) -- sets a module-static
s_psci_use_smc flag to 1 only when method reads exactly "smc"; every
other outcome (no DTB, no PSCI node, method="hvc", property absent)
leaves it at its default 0/HVC, preserving this system's existing QEMU
behavior exactly. arch_cold_reset() now branches on that flag between
smc #0/hvc #0. Called once from apic_init() (arch/aarch64/apic.c), the
one point in boot with boot_info->dtb already in hand -- collocated with
its consumer in arch.c rather than defined in apic.c, since
arch_cold_reset() has no boot_info of its own by the time it runs (called
from deep in VM execution, via BYE).

Three-arch QEMU acceptance run exercised both new guard branches: the
aarch64 boot log shows "PSCI: no DTB -- using HVC (QEMU virt-machine
default)" immediately before the existing GIC discovery lines, then
boots clean to zuse)ok> -- confirming the QEMU/UEFI path is unchanged.
The SMC success branch itself stays unverified until real Pi 5 hardware
runs BYE.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-05 00:06:41 -04:00
Robert Allan JamesandClaude Sonnet 5 dc9445abec apic.c (aarch64): fix GIC base address for real Pi 5 hardware
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
FABRIC-3.md §IV.3 item 7's code audit found GICD_BASE_PA/GICC_BASE_PA
hardcoded to QEMU virt-machine constants (0x08000000/0x08010000),
self-documented as a deliberate exception because QEMU's aarch64 UEFI
firmware never forwards a DTB. That premise doesn't hold on the native
Pi 5 boot path (rpi5_native_boot.c), which does have a real DTB and calls
this same, unmodified apic_init() -- which ignored boot_info entirely and
always programmed the QEMU addresses. Real BCM2712 GIC-400 is at
0x10_7fff9000 (confirmed against bcm2712.dtsi's axi/gicv2 nodes), a
completely different region of the address space. Fixed per direct
instruction, naming this finding specifically.

apic_init() now calls gic_bases_from_dtb() first: fdt_valid(dtb) ->
fdt_find_node_by_compatible(dtb, "arm,gic-400") ->
fdt_find_prop_in_node(..., "reg", ...), reading the first two
2-address-cell/2-size-cell entries (GICD, then GICC -- the standard
arm,gic-400 binding order). Falls back to the QEMU constants on any
failure (no DTB, no matching node), so the existing QEMU/UEFI path is
unchanged. GICD_BASE_PA/GICC_BASE_PA became s_gicd_base/s_gicc_base
(module-static uintptr_t, no longer compile-time constants on this path)
-- every MMIO call site (apic_init, apic_spi_enable, apic_read_iar,
apic_eoi_intid) now reads through them.

Three-arch QEMU acceptance run exercised the guard itself, not just
compiled it: the aarch64 boot log shows "GICv2: no DTB GIC node -- using
QEMU virt-machine defaults" followed by the unchanged "distributor+CPU
interface enabled, PPI 30" line, then boots clean to zuse)ok>. The
success branch (a real DTB with a matching node) stays unverified until
real Pi 5 hardware -- this build never has a devicetree to exercise it
against.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 23:54:39 -04:00
Robert Allan JamesandClaude Sonnet 5 0f798256a0 FABRIC-3.md: aarch64/riscv64 boot-path code audit pass (§IV.3/§V.3 item 7)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Reviewed arch/aarch64/{apic,arch,timer,interrupts}.c and
arch/riscv64/{apic,plic,arch,interrupts,timer}.c for the same class of
QEMU-virt-vs-real-hardware assumption §III item 6's amd64 audit looked for.
Report only, per this project's "identify, don't fix unless asked" rule --
no source files changed.

Findings, aarch64:
- Severe, confirmed live: apic.c's GICD/GICC base addresses are hardcoded
  QEMU-virt constants, self-documented as a deliberate exception because
  QEMU's aarch64 firmware never forwards a DTB. That premise no longer
  holds -- the native Pi 5 boot path (item 2) receives a real DTB and
  calls the same, unmodified kernel_main(), whose M4 sequence calls
  apic_init(boot_info) unconditionally (kernel_main.c:413); apic_init()
  still ignores boot_info entirely. Real BCM2712 GIC-400 is at
  0x10_7fff9000, confirmed against bcm2712.dtsi -- a different region of
  the address space entirely from the hardcoded 0x08000000. With the MMU
  off at this point in boot, this blocks reaching ok> on real hardware as
  the code stands.
- Doc-only correction: interrupts.c's own comment claims VBAR_EL2 is never
  installed ("a known gap"). Checked against isr.S and found stale -- the
  actual implementation already branches on aarch64_current_el() and
  installs vbar_el2/vbar_el1 correctly. No functional gap; the comment
  describes one the code already closed.
- arch_cold_reset() hardcodes the PSCI conduit to HVC, a QEMU-specific
  workaround (QEMU's AAVMF has no genuine EL3). The Pi 5's real ATF means
  EL3 firmware exists there, making SMC the conventional real-hardware
  conduit -- no runtime detection exists.

Findings, riscv64:
- plic.c's QEMU-virt-hardcoded PLIC_BASE is already tracked (§V.3 item 3);
  this pass confirms rather than rediscovers it.
- arch_early_init()'s satp clear is justified entirely by behavior
  observed under QEMU's EDK2 firmware; the native boot path (U-Boot+
  OpenSBI, no EDK2) doesn't share that observation, though the action is
  likely still safe since OpenSBI's handoff conventionally leaves satp=0
  already. Lowest-severity finding in the set.
- Clean: the SBI timer path and arch_cold_reset()'s SBI SRST call are both
  genuinely hardware-independent -- named as the portable pattern amd64's
  i8042-pulse reset and aarch64's hardcoded-HVC PSCI call both lack.

Doc-only change. Three-arch QEMU acceptance (amd64/aarch64/riscv64, in
order) run to confirm non-regression only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 23:31:38 -04:00
Robert Allan JamesandClaude Sonnet 5 6a6ff9353f FABRIC-3.md: amd64 boot-path code audit pass (§III item 6)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Reviewed src/starkernel/boot/uefi_loader.c and every file in arch/amd64/
(arch.c, apic.c, ioapic.c, interrupts.c, i8042.c, timer.c) for anything that
assumes SER5-specific hardware rather than standard UEFI/ACPI. Report only,
per this project's "identify, don't fix unless asked" rule -- no source
files changed.

Verified clean: uefi_loader.c's COM1 presence probe, its two-pass ACPI
2.0-then-1.0 GUID preference, the GetMemoryMap/ExitBootServices golden
path, and GOP BltOnly degradation. apic.c/ioapic.c derive LAPIC mode,
APIC timer frequency, and I/O APIC base/GSI at runtime, not from hardcoded
constants.

Four findings recorded (not fixed):
1. HPET base (timer.c) hardcoded at 0xFED00000, never cross-checked against
   the ACPI "HPET" table -- the same bug class this file's own comment
   already documents and fixed for the PM_TMR port.
2. LAPIC base always 0xFEE00000; apic_init()'s own doc comment flags MADT
   relocation as unimplemented. Narrower than that suggests: ioapic.c's
   parser already reads MadtHeader.local_apic_address, just never plumbs
   it to apic.c. MADT type 5 (Local APIC Address Override) isn't parsed
   at all.
3. i8042 PS/2 controller (i8042.c) and the legacy 8259 PIC (interrupts.c)
   are poked unconditionally with no ACPI FADT presence check, unlike this
   same file's own raw_serial_init() COM1 probe.
4. arch_cold_reset() (confirmed live via BYE, mama_forth_words.c:1437)
   pulses the i8042 reset line instead of using the FADT's standards-defined
   RESET_REG/RESET_VALUE, which doesn't depend on i8042 existing at all.

None are certain to bite on the real SER5 -- the conventions assumed hold
on the large majority of PC-compatible x86_64 systems -- but the decision
in FABRIC-3.md §III requires arguing genericity from standards compliance,
not from "it booted," and these are the concrete gaps.

Doc-only change. Three-arch QEMU acceptance (amd64/aarch64/riscv64, in
order) run to confirm non-regression only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 23:15:09 -04:00
Robert Allan JamesandClaude Sonnet 5 ecdb32e0d6 boot_media/rpi5/config.txt: Pi 5 native boot config (FABRIC-3.md §IV.3 item 6)
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Researched against the official raspberrypi.com config.txt reference and one
real, working Pi 5 bare-metal project's own checked-in config.txt
(leopoldch/BatMetal), not assumed from general Pi knowledge:

- kernel=kernel_2712.img, os_check=0, device_tree=bcm2712-rpi-5-b.dtb
- enable_uart=1 added (correction to the item's original text): costs
  nothing when unused, and is the only diagnostic channel that would survive
  a framebuffer failure on the untested mailbox path (rpi5_native_boot.c).
- arm_64bit=1 dropped (correction to the item's original text): confirmed
  inert on Pi 5 by official docs.
- dtparam=pciex1 researched, deliberately omitted with rationale recorded --
  community-reported only, not needed until item 5's re-scoped RP1/PCIe work.
- os_check=0 vs. BatMetal's own config (which omits it) recorded as an open,
  unresolved discrepancy rather than silently picked.

boot_media/rpi5/README.md notes the one remaining gap this exposes:
kernel_2712.img names a file nothing currently builds (item 1's
separate-image build target is still outstanding future work).

Doc-and-config only, no compiled code changed. Three-arch QEMU acceptance
(amd64/aarch64/riscv64, in order) run to confirm non-regression only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 23:05:39 -04:00
Robert Allan JamesandClaude Sonnet 5 11c93773d0 FABRIC-3.md: pci_init() DTB path item was wrong -- re-scope, don't build the ECAM version
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Investigated before writing code, per this document's own discipline, and
found the original item 5 text's assumption doesn't hold: it framed this
as "swap the ACPI MCFG lookup for a DTB one, same flat-ECAM access
underneath." Confirmed against three primary sources that this is wrong:

- bcm2712-rpi-5-b.dts: RP1 sits under pcie2 ("brcm,bcm2712-pcie"),
  reg = <0x10 0x00120000 0x00 0x9310> -- a ~37KB window, far too small
  for a flat 256MB ECAM region.
- pcie-brcmstb.c (the real Linux driver for this compatible string):
  brcm_pcie_map_bus() computes a standard ECAM-shaped offset but accesses
  it through an IDX_ADDR/DATA_ADDR indirect index+window pair, not a
  direct MMIO read. A real third config-access mechanism, not a bigger
  version of the DTB-lookup task.

Re-scoped item 5 (§IV.3) to record the real shape: pci.c's cfg_read32/
cfg_write32 family dispatches today via #ifdef ARCH_AMD64 vs. everything
else (flat ECAM) -- but QEMU aarch64 (flat ECAM, real ACPI MCFG) and Pi 5
aarch64 (Broadcom indirect windowing) are the same build, so this needs a
runtime dispatch change to a file all three architectures currently boot
through, not a small addition. Also flagged: pci_init()'s vmm_map_range()
for the ECAM window is amd64-only today (aarch64/riscv64 rely on UEFI's
identity map); native boot has no UEFI identity map at all, so any PCIe
work here needs its own explicit mapping regardless of access mechanism.

Also corrected §V.3 item 5's now-stale "shares the same code" cross-
reference -- the Mars's JH7110 PCIe controller needs its own primary-
source check, not an assumed-shared implementation with RP1's.

Not implemented this pass -- recording the real shape is the deliverable;
building the runtime-dispatch version is a materially larger, separate
task. Doc-only change; 3-arch boot verified per the project's standing
rule, proving only that nothing regressed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 22:47:18 -04:00
Robert Allan JamesandClaude Sonnet 5 7187d68082 rpi5_native_boot.c: carve /reserved-memory out of the Pi 5 memory map
Build / build-amd64-iso (push) Waiting to run
Build / build-aarch64-iso (push) Waiting to run
Build / build-riscv64-img (push) Waiting to run
Previously deferred (rpi5_native_boot.c's own header comment flagged this
as needing interval-splitting logic written blind against hardware not
yet in hand) -- revisited by fetching bcm2712-ds.dtsi directly rather than
assuming reserved-memory was empty or absent. It has one static child
(atf@0, ARM Trusted Firmware's own region) and one dynamic child
(linux,cma, size/alloc-ranges only, no fixed reg) -- the dynamic one is
skipped, nothing fixed to carve and no allocator this early to service it
against anyway.

Adds two fdt.c primitives: fdt_find_node_by_name() (reserved-memory has
neither compatible nor device_type per DT spec) and fdt_next_child_node()
-- one exported symbol, not the two-primitive general sibling-walker
originally sketched, collapsed after review since the only real use here
is "iterate one node's direct children."

collect_reserved_ranges() reads each child's own #address-cells/
#size-cells with a fallback to root's only if absent -- confirmed
necessary, not just defensive: reserved-memory's own declared <2>/<1>
genuinely differs from root's <2>/<2>. emit_region_with_carveouts()
clips a sorted reserved-range list against each RAM region, emitting
alternating EfiConventionalMemory gaps and EfiReservedMemoryType
carve-outs (insertion sort, no libc qsort in freestanding).
RPI5_MAX_MEMMAP_ENTRIES is the exact worst-case count, recomputed rather
than estimated -- the rpi5_mailbox.c buffer-size bug is the standing
lesson for this pattern.

Compile-only-verified; nothing in the existing UEFI/QEMU path calls
rpi5_native_boot(), so this cannot be exercised until real hardware.
Verified 3-arch boot to ok>/zuse)ok>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 22:24:57 -04:00
Robert Allan JamesandClaude Sonnet 5 67e3fb7459 rpi5_native_boot.c: Pi 5 DTB->BootInfo constructor (FABRIC-3.md §IV.3 item 2)
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
rpi5_native_boot() populates the existing BootInfo struct from the
devicetree instead of UEFI protocols, then calls the existing, unmodified
kernel_main() -- the crux of why most of M1-M9 stays shared between the
UEFI and native boot paths. native_rpi5_entry.S now tail-calls into it
instead of halting.

memory_map is built from /memory's own reg, honoring the *root* node's
#address-cells/#size-cells (confirmed against bcm2712.dtsi's actual root
node -- <2>/<2> -- not assumed; a wrong cell width here would compile and
boot clean in QEMU while silently corrupting the real memory map on real
silicon). Required a new fdt_find_node_by_device_type() since /memory is
identified by device_type = "memory" per DT spec, not compatible.

args comes from /chosen's bootargs fed into the existing
cmdline_parse_ascii() (confirmed pure C99 with no UEFI coupling before
reusing it). framebuffer comes from the already-built
rpi5_mailbox_get_framebuffer() at a fixed 1920x1080x32 default -- no EDID
query exists in this codebase, flagged rather than guessed past.

Deliberately scoped out, not silently skipped: /reserved-memory is not
parsed. Carving reserved sub-ranges out of /memory's span needs
interval-splitting logic that would be written blind against hardware not
yet in hand -- exactly the kind of code that hides a bug until real
silicon. pmm.c's Pass 3 only ever clears pages this file lists as
EfiConventionalMemory, so the gap is "less usable RAM than optimal," never
"reserved RAM wrongly marked free." Left as its own future item.

Also fixes a real link failure this work surfaced: boot/cmdline.c was only
in LOADER_SRCS_BASE (the .efi target), not KERNEL_SRCS_BASE (the separate
.elf target arch/aarch64/*.c also wildcards into) -- added it there too.

Compile-only-verified; nothing in the existing UEFI/QEMU path calls
rpi5_native_boot(), so this cannot be exercised until real hardware.
Verified 3-arch boot to ok>/zuse)ok>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 14:42:32 -04:00
Robert Allan JamesandClaude Sonnet 5 281de9547c native_rpi5_entry.S: Pi 5 native (non-UEFI) boot entry stub (FABRIC-3.md §IV.3 item 1)
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
rpi5_native_start masks x0 down to the documented 32-bit DTB-pointer range
(the firmware's own entry protocol leaves the upper 32 bits unspecified),
stores it into g_rpi5_dtb_ptr for the still-open DTB->BootInfo constructor
(item 2) to read, then switches sp to a dedicated 2 MiB BSS stack -- this
path has no EDK2 boot stack to inherit, unlike every other entry path in
this codebase.

Intentionally halts (wfe/b loop) afterward rather than tail-calling into
item 2's constructor, which doesn't exist yet -- no stub function pretending
to be more than it is.

Not yet linked at the real 0x80000 load address; that needs its own linker
script/build target, not scoped into this item. Compiles and links into the
existing ARCH=aarch64 QEMU/UEFI acceptance build as dead code (ELF kernel
build's KERNEL_ASM wildcards every *.S in arch/aarch64/; nothing there
branches to it), same as rpi5_dtb.c/rpi5_mailbox.c before it.

Verified 3-arch boot to ok>/zuse)ok>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 14:12:42 -04:00
Robert Allan JamesandClaude Sonnet 5 a32b0ebcbe rpi5_mailbox.c: wire the VideoCore mailbox message protocol (FABRIC-3.md §IV.3 item 3)
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
New rpi5_mailbox_get_framebuffer() sends one property-tag request buffer
(phys size, virt size, depth, pixel order, virtual offset, allocate-buffer,
get-pitch) over the register layout rpi5_dtb.c already discovers, populating
an Rpi5FramebufferInfo kept in exact field-for-field sync with uefi.h's
FramebufferInfo so console.c/vt100.c/framebuffer.c need no downstream changes
once this is wired into a real entry stub.

Register offsets (+0x00/+0x18 MBOX0 read/status, +0x20/+0x38 MBOX1
write/status) confirmed against a Pi-5-specific bare-metal reference,
independently cross-checked against this codebase's own rpi5_dtb.c
translated base address.

Caught and fixed a real buffer-overflow bug before compiling: the static
request buffer was sized 32 words against an actual 35-word requirement for
the 7-tag sequence, recomputed exactly rather than re-estimated; resized to
40 words.

Two things flagged as genuinely unverified against real hardware (not
guessed past the comment): the allocate-buffer tag's request-size field
value, and whether its response address needs classic bus-alias masking on
Pi 5 specifically.

Compile-only-verified -- no caller yet (that's the still-open entry-stub/DTB
constructor items). Verified 3-arch boot to ok>/zuse)ok>.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 14:00:32 -04:00
Robert Allan JamesandClaude Sonnet 5 ca52ce8243 rpi5_dtb.c: wire fdt.c's node-scoped lookup into the Pi 5 UART/mailbox addresses
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
New include/starkernel/rpi5_dtb.h / src/starkernel/arch/aarch64/rpi5_dtb.c:
rpi5_uart_base()/rpi5_mailbox_base(), each finding their peripheral by
compatible string ("arm,pl011" / "brcm,bcm2835-mbox") via fdt_find_node_by_
compatible() then reading its "reg" via fdt_find_prop_in_node().

Found and fixed a real translation gap before it could have silently
produced a wrong address: confirmed directly against bcm2712.dtsi
(raspberrypi/linux) that both peripherals live under one "soc"
simple-bus node whose ranges property adds a fixed 0x10_0000_0000
offset to every child reg value. fdt.c's reader deliberately doesn't
apply ranges translation generally (not a general devicetree
library); this file applies that one, fixed, SoC-wide offset
explicitly, documented with the exact devicetree excerpt that
confirmed it.

Compile-only verification -- no caller wired in yet, these two
functions are what the still-open entry-stub and mailbox-framebuffer-
driver punch-list items will call. Verified 3-arch boot to ok>
(amd64/aarch64/riscv64, each in the foreground; rpi5_dtb.o confirmed
built on aarch64, the only arch that compiles this file).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 13:36:08 -04:00
Robert Allan JamesandClaude Sonnet 5 5e46f18fd9 fdt.c: node-scoped lookup extension (FABRIC-3.md SSIV.3/SSV.3 shared item)
Build / build-riscv64-img (push) Canceled after 0s
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Adds fdt_find_node_by_compatible() and fdt_find_prop_in_node() to the
minimal FDT reader -- the extension fdt.h's own header comment already
flagged as a known future need ("item 0.6 will need node-scoped reg
lookups"), now with real consumers: the Pi 5's UART/mailbox register
addresses (native boot, no ACPI) and the Milk-V Mars's real PLIC base
address (currently hardcoded to QEMU-virt's own value).

fdt_find_node_by_compatible() matches any entry in a node's
NUL-separated "compatible" list, first match in document order.
fdt_find_prop_in_node() scopes to that one node's own direct
properties only -- stops at the first child node or the node's own
end, per the DT spec's ordering guarantee that a node's properties
always precede its children. Same minimal, non-tree-building,
single-linear-scan-per-call style as the existing reader; no new
state, no allocation.

Compile-only verification -- no caller wired in yet, this is the
shared primitive both boards' own punch-list items will call once
built. Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in
the foreground).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 13:05:42 -04:00
Robert Allan JamesandClaude Sonnet 5 bffd87615d FABRIC-3.md: riscv64 (Milk-V Mars) planning decided + punch list
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Resolved SS V.1's flagged gap: standard RISC-V SBI boot protocol,
confirmed via OpenSBI's own docs -- a0=hart ID, a1=DTB pointer, S-mode
entry, universal across FW_DYNAMIC firmware regardless of vendor, not
chain-specific guesswork.

Decided: observation is HDMI-only, same reasoning and constraint as
the Pi 5 (no bridge hardware for this board's own first bring-up
either).

Traced boot_info->acpi_table's real riscv64 consumers the same way as
aarch64: pci_init() again (Mars's M.2 slot is PCIe-attached, same
shape of gap as the Pi 5's RP1); timer.c is already fully DTB-driven,
no work needed there.

One real, already-flagged risk found while tracing this: PLIC_BASE is
a QEMU-virt-specific constant, not DTB-discovered -- arch/riscv64/
plic.c's own doc comment already warned about this; it becomes
concrete now that real hardware is actually in scope. Real punch-list
item, not hypothetical.

6-item no-hardware-needed punch list (entry stub, DTB->BootInfo
constructor, DTB-discovered PLIC base, unresearched JH7110 framebuffer
flagged honestly rather than assumed, shared pci_init() DTB path with
the Pi 5, image-packaging tooling) plus 5 items deferred to
2026-09-17.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:55:28 -04:00
Robert Allan JamesandClaude Sonnet 5 1ec2a29384 FABRIC-3.md: aarch64 (Pi 5) planning decided + punch list
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Decisions from conversation: observation is HDMI-only for this
board's own bring-up (no second Pi, no dedicated USB-serial adapter;
using the Milk-V Mars as a bridge before it's independently validated
would be circular). Both boards arrive 2026-09-17, giving real runway
to finish design/code work first ("plan well before doing").

Traced boot_info->acpi_table's actual aarch64 consumers against real
code before writing the punch list: only pci_init() (unconditional,
relevant since RP1 is PCIe-attached) and this session's own
running_under_hypervisor() (already degrades safely on NULL). GIC
init (arch/aarch64/apic.c) already only tries boot_info->dtb, never
acpi_table -- Pi 5 native boot removes its stated blocker for free.

6-item no-hardware-needed punch list: entry stub at 0x80000, a DTB ->
BootInfo constructor calling the existing unmodified kernel_main()
(the crux of why most of M1-M9 stays shared), the mailbox-interface
framebuffer driver, an fdt.c node-scoped-lookup extension (its own
header already flagged this as a known future need), a DTB-based
pci_init() path for RP1, and config.txt contents. Plus 5 items
deliberately deferred until hardware is in hand 2026-09-17.

Also fixed a formatting slip from an in-progress edit (a bullet
accidentally turned into a malformed heading) before it could compound.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:52:37 -04:00
Robert Allan JamesandClaude Sonnet 5 9142d50b73 FABRIC-3.md: Zynq-7000 (Puzhi PZ7010/PZ7020 StarLite) added to hardware reference
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Recorded only, no work scoped -- but unlike BeagleBone Black, this
one isn't a random addition: ROADMAP.md already names Zynq FPGA as
the next big milestone beyond v2.5.0 (the "configurable silicon"
step, tied to the three-product split's HOL-proven sealed-execution
hardware product). This puts a concrete, purchasable board under that
already-named milestone: Puzhi PZ7010-StarLite (XC7Z010) or
PZ7020-StarLite (XC7Z020), dual-core Cortex-A9 PS + Artix-7/Kintex-7
PL fabric, DDR3/JTAG/UART/HDMI/GbE/USB2.0/40-pin, MIPI CSI on the
7020 variant only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:44:57 -04:00
Robert Allan JamesandClaude Sonnet 5 5eeb993ca2 FABRIC-3.md: riscv64 boot chain resolved, BeagleBone Black noted, header bug fixed
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Milk-V Mars boot chain resolved via VisionFive 2 research: the Mars is
a documented mainline U-Boot target using the exact same binaries as
the VisionFive 2 (same StarFive JH7110 SoC) -- U-Boot + OpenSBI +
devicetree, not UEFI, same fork already decided for aarch64. Entry
point 0x40000000, mhartid CSR for core ID, UART at 0x10000000/115200 --
DTB pointer/hart-ID register convention at actual kernel entry not yet
confirmed for this specific chain.

Fixes a real bug from the earlier aarch64 edit: the "## V. riscv64 --
Milk-V Mars" section header had been accidentally dropped, leaving a
stale duplicate "Already true"/"Genuinely open" block sitting where
the real section should have started. Removed the duplicate, restored
the header, folded today's research into the section properly.

BeagleBone Black added to SS VI's hardware-identification reference per
direct instruction -- recorded only, no work scoped around it: it's a
32-bit ARM (TI AM335x, Cortex-A8) SoC, a genuinely different, fourth
architecture this kernel has no support for, not another board under
an existing one.

FABRIC-4.md SS2: captured the pinned-GPIO-VM idea raised in conversation
(Pi 5 needs one, Milk-V Mars explicitly undecided) as a theory-stage
note, cross-referenced from SSIV/SSV's own open-questions lists.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:43:15 -04:00
Robert Allan JamesandClaude Sonnet 5 987c751352 FABRIC-3.md: per-board hardware-identification reference (SS VI)
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Consolidated SoC/CPU/RAM/I-O facts for all three real-hardware
targets, researched via web search with sources cited rather than
assumed from memory:

- amd64 (Beelink SER5): Ryzen 7 family confirmed; exact SKU
  (5700U/5800H/7735HS all shipped under this branding) not yet
  confirmed against the actual unit -- flagged, not guessed.
- aarch64 (Raspberry Pi 5): BCM2712, quad-core Cortex-A76 @ 2.4GHz,
  VideoCore VII GPU, LPDDR4X-4267. FEAT_RNG (RNDR) presence on this
  core flagged as unconfirmed either way.
- riscv64 (Milk-V Mars): StarFive JH7110, 4x SiFive U74-MC + 1x S7
  monitor core, up to 1.5GHz. Noted it shares its SoC with the
  StarFive VisionFive 2 -- existing VisionFive 2 bring-up material
  may transfer directly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:37:29 -04:00
Robert Allan JamesandClaude Sonnet 5 a66af477ac FABRIC-3.md: aarch64 boot-chain decision -- native boot flow, not UEFI
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Researched both options before deciding. The UEFI path (rpi5-uefi,
TF-A+EDK2, SBBR-compliant) is real but archived since 2025-02-04 --
support ended when newer Pi EEPROM firmware broke compatibility, and
its own README says ACPI support is limited/incomplete. Decided:
native boot flow instead (config.txt/kernel_2712.img/DTB, x0=DTB
pointer at entry, no ACPI at all).

Named the real scope rather than estimating it small: a new,
non-UEFI aarch64 entry path, a DTB-driven BootInfo equivalent, and a
new mailbox-property-interface framebuffer driver (no precedent in
this codebase). The one genuine piece of reusable groundwork:
starkernel/hal/fdt.c's minimal FDT reader, already built for
riscv64's timebase-frequency lookup, extends directly to Pi 5
peripheral discovery.

Also documents the peripheral-RNG research: BCM2712 has no
brcm,bcm2712-rng200 (or equivalent) entry anywhere in current
mainline Linux, and RP1's own published peripheral list doesn't
mention an RNG -- genuinely unresolved, not just under-researched.

Not yet turned into a punch list -- the boot-chain fork's own shape
needs thinking through first.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:34:30 -04:00
Robert Allan JamesandClaude Sonnet 5 e5e28d5198 FABRIC-3.md: amd64 bare-metal-boot planning decided + punch list
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Decisions from conversation: observation is HDMI (interactive) +
serial via the Raspberry Pi's own GPIO UART as the bridge, if the SER5
exposes a UART header (not yet confirmed); genericity is verified by a
code audit against real UEFI/ACPI standards rather than a second
physical machine (none available); Secure Boot is already disabled on
this SER5, so no signed-loader work is needed for this pass.

9-item punch list follows: build+flash the thumbdrive image, physically
inspect for a UART header, connect HDMI, boot, a code-audit pass for
SER5-specific assumptions (the actual genericity bar), capture the
boot (POST/ok>/rdrand backend/serial transcript), mint+re-attach Zuse
on real hardware, then update this section with results before moving
to aarch64.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:27:38 -04:00
Robert Allan JamesandClaude Sonnet 5 7f9a4d4d4a FABRIC-3.md: three per-architecture bare-metal-boot planning sections
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
Per direct instruction: amd64's real goal is genericity (any x86_64
laptop/desktop/tower/mini, not just the Beelink SER5 reference
machine); aarch64 targets the Raspberry Pi 5 exclusively; riscv64
targets the Milk-V Mars exclusively -- no cross-board genericity
requirement for the latter two, unlike amd64.

Each section starts from what's already true (ROADMAP.md's existing
v2.2.0/v2.4.0/v2.5.0 board-by-board gates, the already-built
thumbdrive/iso-usb Makefile targets, the amd64 RDRAND backend) and
names what's genuinely still unknown rather than assuming -- most
notably whether the Milk-V Mars boots via UEFI (like this project's
QEMU riscv64 target) or via U-Boot+OpenSBI+devicetree, which would
need a different boot entry path, not just different peripheral
addresses.

Doc-only change, no acceptance build needed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 12:02:45 -04:00
Robert Allan JamesandClaude Sonnet 5 8717416d36 FABRIC-3.md: version correction -- LITHOS_VERSION back to 2.0.0, plus a rename-gap fix
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
LITHOS_VERSION 2.0.1 was premature: per this project's own versioning
policy, 2.0.1 claims SER5 hardware-track progress (RDRAND backend +
thumbdrive image) that was never actually verified on real hardware --
that verification is FABRIC-3.md's own open topic. Reset to 2.0.0
(still a QEMU-only release, correctly). Verified 3-arch boot shows
"LithosAnanke v2.0.0" in each serial log directly, not assumed from the
Makefile edit alone.

Also closes a real gap found in today's earlier FABRIC-series rename:
Makefile.starkernel, Kconfig.kernel, scripts/bleach_zuse_img.sh, four
proof/*.thy files, and isr.S were never swept -- the original file list
only matched *.md/*.c/*.h/*.4th, silently skipping every other
extension. Fixed with the same safe placeholder substitution.
.claude/settings.local.json's historical permission-grant log and
ClaudeEXPORT/'s frozen export were deliberately left untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:53:44 -04:00
Robert Allan JamesandClaude Sonnet 5 fcba528273 FABRIC-3.md: Task 1 closed -- master fast-forwarded to v2.0.1, verified
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
master (d2a0305) was a strict ancestor of v2.0.1 (72c14cb) -- pure
fast-forward, no real merge needed. Fast-forwarded local master,
pushed to origin (58c59e8..72c14cb). Verified on a genuinely clean
master tree rather than inferring from the fast-forward: hosted build
clean, 3-arch kernel acceptance (amd64/aarch64/riscv64, each in the
foreground) all reached ok>/zuse)ok> and shut down cleanly, identical
to v2.0.1's own last acceptance pass. master and v2.0.1 are now
identical on both local and origin.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:37:29 -04:00
Robert Allan JamesandClaude Sonnet 5 72c14cb9eb FABRIC-3.md: open new living document (bare metal boot); close FABRIC-2.md
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s
FABRIC-2.md closed/archival as of 2026-09-04 -- its full punch list
(§I) was worked through to completion this session. FABRIC-3.md opens
as the new living document, topic: bare metal boot (FABRIC-2.md §I.6's
own Milestone 8, the one item that couldn't close from a coding
session -- needs a real machine and a human present).

First task written up before executing, per this series' own standing
discipline: merge v2.0.1 into master and verify build/function
equivalence on a clean master tree. Investigated the branch topology
first -- master is a strict ancestor of v2.0.1 (47 commits behind, no
divergent history), so this is a pure fast-forward, not a real merge.

Updated FABRIC-4.md's and .claude/CLAUDE.md's stale pointers at
FABRIC-2.md as "current/living" to point at FABRIC-3.md instead.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:31:04 -04:00
Robert Allan JamesandClaude Sonnet 5 b031b802e3 Rename FABRIC series: FABRIC.md->0, FABRIC-2.md->1, FABRIC-3.md->2, FABRIC-4.md unchanged
FABRIC.md -> FABRIC-0.md
FABRIC-2.md -> FABRIC-1.md
FABRIC-3.md -> FABRIC-2.md (the current/living document)
FABRIC-4.md unchanged (new #3 to follow separately)

Every cross-reference repo-wide updated to match, including doc-comment
citations inside kernel source (.c/.h) files -- done via an ordered
placeholder substitution (FABRIC-3.md->placeholder2, FABRIC-2.md->
placeholder1, FABRIC.md->placeholder0, then placeholders resolved to
final names) in a single pass per file to avoid double-shifting
already-renamed references.

One line in capsules/font.4th grew past the 64-char block-format limit
as a side effect of the longer filename; shortened it and reverified
with mkcapsule --lint (34/34 pass) before rebuilding.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground) after the fix; logs and DoE CSVs from this session's
verification runs included per this repo's own audit-artifact
convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:22:51 -04:00
Robert Allan JamesandClaude Sonnet 5 ff2941dfb9 FABRIC-3.md §I.7: close Milestone 9 deferral now that its precondition is met
The item only ever tracked whether the deferral itself was still
correctly in force, not the networking work. Milestone 7 (§I.5) closed
this session, satisfying the precondition, so the deferral resolves --
Milestone 9's actual networking design stays a separate, still-unscoped
effort, correctly out of scope on its own terms (FABRIC-2.md's own
sequencing: after ACL/PKI/contrib, not concurrent with it).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:05:51 -04:00
Robert Allan JamesandClaude Sonnet 5 eeceec21a5 FABRIC-3.md §I.5: Milestone 7 trust tiers (QEMU-vs-real-hardware), closing it
Closes the contributor-capsule/trust-tier punch-list item. Decided
direction: QEMU-vs-real-hardware conditional enforcement.

Found before building on that decision: the obvious mechanism (expose
TimerInfo.vm_mode) only works on amd64 -- aarch64 and riscv64 both had
vm_mode hardcoded to 1 unconditionally, meaning they'd always report
"running under QEMU" even on real hardware. Built real detection for
both instead of shipping that: aarch64 checks the ACPI RSDP's OEM ID
for QEMU's "BOCHS " SeaBIOS-heritage signature; riscv64 checks the
devicetree root compatible property for "qemu". Confirmed vm_mode was
otherwise unread anywhere else in either file first -- zero risk to
existing timing behavior.

CAPSULE_FLAG_CONTRIB (mkcapsule.c: FLAG_CONTRIB) path-matches on
capsules/contrib/, mirroring FLAG_MAMA_INIT's exact-match pattern.
contrib_capsule_refused() (capsule_birth.c) enforces: no additional
check under QEMU (same WARN-only as everything else); on real hardware,
a contrib capsule additionally requires CAPSULE_SIG_OK, since it has no
other provenance to fall back on. Wired into capsule_birth_baby() and
capsule_run_experiment().

Also updates §I.7 (Milestone 9): its stated precondition (Milestone 7
closing) is now met, flagged as stale rather than treated as a green
light to design networking from nothing.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground) -- compile/boot verification only; the real-hardware
enforcement branch is unverifiable from this environment, same as all
of §I.6. logs and DoE CSVs from this session's verification runs
included per this repo's own audit-artifact convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 11:04:25 -04:00
Robert Allan JamesandClaude Sonnet 5 5567d03c12 FABRIC-3.md §I.4: magic-number content-type detection, closing Milestone 6
Closes both remaining Milestone 6 (PKI) punch-list items.

Signature-status column: doc-only closure -- the underlying need was
already redirected to capsules/BLOCK_MAP.md's real Signed column
(2026-08-26); checking the box off as moot-as-worded rather than
leaving an accurate-but-permanently-unchecked marker.

Magic-number content-type detection (Section U item 14): built in
tools/mkcapsule.c. detect_content_type() classifies a file's actual
leading bytes (TTF/OpenType sfnt tags, DER's 0x30 SEQUENCE tag, or a
printable-ASCII/TAB/CR/LF heuristic for text) against
expected_type_from_ext()'s .4th/.md/.der/.ttf mapping; process_file()
warns on mismatch, never refuses -- same WARN-first rollout this
project already used for capsule signing. Verified against every real
capsule in the repo (38 files) with zero false positives. This is the
shared primitive Milestone 7's contrib-capsule validation can reuse
next.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground); logs and DoE CSVs from this session's verification runs
included per this repo's own audit-artifact convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 10:45:57 -04:00
Robert Allan JamesandClaude Sonnet 5 4018fe8b04 FABRIC-3.md §I.2: FIRSTTOUCH + migration state machine (blk_meta_relocate_devblock)
Closes the block-subsystem punch-list item -- built exactly to §F.11's
already-decided algorithm after re-verifying it against current
blk_meta_t (a 2026-09-03 re-scoping note had wrongly claimed the chain
fields no longer existed; they do, untouched by BMAPFMT).

blk_firsttouch_claim(): one linear scan of Artemis's own device
(new blk_get_first_disk_range(), correctly bounding the scan instead of
the global multi-device LBN space), scattered-chain claim via
prev_block/next_block/chain_length, owner_fp stamped on every member
devblock, fails outright with no partial claim.

blk_meta_relocate_devblock(): the real migration primitive -- bridges
the existing FORTH-block-granularity blk_subsys_relocate_block() up to
devblock granularity (BLK_PACK_RATIO=3, corrected mid-design), running
it 3x and transferring blk_meta_t ownership fields. The "migration
state machine" turned out to be just the 2 states BLK_FLAG_MIGRATING
already reserved; the real design work was the trigger. Two were
scoped in conversation (overflow onto Artemis; heat-based wear
leveling); heat/wear-leveling is built and wired into sk_repl_idle()
via blk_meta_t.write_count. Overflow is deliberately left open,
precisely scoped (needs a slot-lookup-by-device-pointer call site
threaded from WIREBIND) rather than guessed at.

Also flagged, not fixed: BMAPFMT's owner_fp/CLAIMED and the pre-existing
BAM allocator are two parallel, unreconciled accounting systems --
FIRSTTOUCH/relocate only touch the former.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground); logs and DoE CSVs from this session's verification runs
included per this repo's own audit-artifact convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 10:33:47 -04:00
Robert Allan JamesandClaude Sonnet 5 1d468a65b1 FABRIC-3.md §I.1: (user) console prompt segment, closing the 4.4s->4.3->1.11 chain
Extends the REPL prompt to "[VM name] (user) ok>" (e.g. "[Hera]
(zuse) ok>") per the locked FABRIC.md §4.4s spec, unblocked by this
session's own §I.3/§I.8 identity-tracking work. Adds
capsule_wirebind_attached_username() alongside the existing tracked
VMUuid, and a new sk_print_prompt() helper (repl.c) that all three
prompt call sites now go through -- checks Zuse first, then a WIREBIND
user, prints nothing when neither is attached.

Closes 4.4s, 4.3 (console umbrella), and formally settles 1.11
(dirty-event granularity) as region-based per FABRIC-2.md's own "no
independent path" ruling -- a decision closure only, not an
implementation, so §17.4 (framebuffer heat/decay physics) stays open,
re-scoped precisely: blocked on the dirty-region-tracking mechanism
existing, not on 1.11's decision.

Documents a reported-but-unreproduced terminal defect (§I.9) as a new
punch-list item -- investigated the readline/keyboard-bridge code
paths, found nothing conclusive, needs a live repro with a serial log
before it's actionable.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground); logs and DoE CSVs from this session's verification runs
included per this repo's own audit-artifact convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 09:42:26 -04:00
Robert Allan JamesandClaude Sonnet 5 60d9c2520e FABRIC-3.md §I.3/§I.8: WIREBIND EJECT/detach + EXPIRE re-scoped as logout
Closes §I.3 (Milestone 5 remainder): WIREBIND now tracks which VM is
attached via the home-blocks USB path, and a new EJECT word plus the
existing hot-unplug signal both flush/reset-console/kill through it
(FABRIC-3.md §F.10).

Closes §I.8 (EXPIRE/ACL), re-scoped: the original "admit the zuse
session as a Stadium patron and reap on TTL" plan was invalidated a
second time -- Zuse authenticates directly onto Hera, who is patron
zero and permanently pinned, so there is no patron for a reap sweep to
ever find. Built instead as a detach-triggered logout
(capsule_zuse_boot_logout()), the same trigger EJECT/hot-unplug use for
regular WIREBIND users, so neither identity is a special case. Required
a companion fix: install_and_activate() used to skip re-running
ACL-ZUSE-BOOT whenever the cert was already installed, which made a
logout permanent for the rest of the boot; the outer re-attach gate now
checks zuse_session (clears on logout) instead of zuse_cert_installed
(a deliberate permanent one-way ratchet, left untouched).

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground) after both steps; logs and DoE CSVs from this session's
verification runs included per this repo's own audit-artifact
convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
2026-09-04 09:21:01 -04:00
Robert Allan JamesandClaude Sonnet 5 34203613bb FABRIC-4.md: new forward-looking design-notes scratchpad
Not a successor to FABRIC-3.md (still the living document) -- a
separate, lower-discipline space for theory-stage ideas caught before
they have real scope. First entry: the Stadium-level "wheel" idea from
conversation with Captain Bob -- a second, Q48.16 phase-accumulator
clock fed by RWOT + the inference engine, meant to schedule when a
future sieve-stack slips in, rate starting fixed and graduating to
adaptive later, mirroring L8's own hardcoded-to-inferred history.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 14:11:08 -04:00
Robert Allan JamesandClaude Sonnet 5 b16b54f5a8 FABRIC-3.md: add §I, consolidated live punch list of open items
Groups the 23 confirmed-still-open items from the 2026-09-03 stale-
checkbox audit (commit 403f53d) by subsystem instead of scattered
document chronology, each pointing back to its full original entry.
Doesn't replace §A/§B as historical record -- it's the live index;
closing an item here should also update its original location.

Two findings surfaced while writing this:
- The block-map first-touch allocation item's own described mechanism
  (blk_meta_t prev_block/next_block/chain_length) is stale -- those
  fields don't exist since §H.12's BMAPFMT rebuild; flagged for
  re-scoping against the current layout.
- EXPIRE (ACL)'s stated blocker ("Phase 8 doesn't exist yet") is now
  stale -- Phase 8 closed 2026-08-26/27 -- but the real remaining gaps
  (no zuse-session lifetime/logout, no Stadium generic ttl-reap
  mechanism) are narrower and still real, so it's flagged, not unblocked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 13:48:33 -04:00
Robert Allan JamesandClaude Sonnet 5 403f53d056 FABRIC-3.md: retroactively check 11 stale-carried-forward items
Audited all 34 unchecked checklist lines against the actual codebase.
Checked off 11 that were already done -- most were completed in
FABRIC-2.md/FABRIC-3.md sections written after the item's own carry-
forward, never reconciled back to the original checkbox: the DoE
Stadium-substrate re-run (5.1), xHCI stall recovery (G.1), CERTVERIFY,
the whole BINDSTEP cluster (key/lock design, hotplug-to-birth wiring,
USE guard), and the Milestone 6 PKI generation/embedding/signing steps.
Each gets a RETROACTIVELY CHECKED annotation citing real file:line
evidence. One item (MANIFEST_AUTO.md signature column) investigated and
left unchecked on purpose -- the goal was met via a different file
(BLOCK_MAP.md's Signed column), not the one this item names.
The other 23 unchecked items were verified still genuinely open (several
require real hardware, not verifiable from code) and left untouched.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 13:34:29 -04:00
Robert Allan JamesandClaude Sonnet 5 2b9fa02354 §H.12 steps 21-22: ELEVATE-REQUEST + ELEVATE-GRANT + SEND-ELEVATE-REQUEST
Phase 7 complete, closing out §H.12's punch list. MSG-DELIVER turned out
to VM-EXEC payload text directly rather than dispatching by type, so the
"handler" is ELEVATE-GRANT, a word the delivered text calls. New Hera-only
C primitives (ZUSE-ELIGIBLE?, NAME>XT, ELEVATE-PUBKEY-UNPACK) stay plain
and unconditional; capsules/zuse-eligibility.4th composes the actual
eligibility check + ACL-ALLOW!/ACL-TTL! grant in FORTH.
SEND-ELEVATE-REQUEST (common:messaging.4th) builds the payload text and
sends it via the item-20-gated CH-REQUEST.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 13:15:40 -04:00
Robert Allan JamesandClaude Sonnet 5 cb6e079a73 §H.12 step 20: CH-REQUEST initiator-only gate (MY-CH-ID)
Added VARIABLE MY-CH-ID to messaging.4th (fail-closed -1 default) and
set it per-VM in hermes/init.4th and artemis/init.4th. CH-REQUEST now
refuses if the caller-supplied 'from' doesn't match the calling VM's
own id, closing a real spoofing gap found while implementing this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 12:09:29 -04:00
Robert Allan JamesandClaude Sonnet 5 7d53344875 §H.12 step 19: ZUSE-ELIGIBILITY-ADD word, no gating (corrected mid-step)
Plain FORTH word wrapping zuse_eligibility_add() unconditionally. Two
wrong first attempts (C-level zuse_session check, then a FORTH wrapper
checking it) both corrected: zuse_session isn't a special axis needing
its own gate anywhere -- Zuse's authority is the absence of any ACL
restricting her, not a flag any word checks.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 11:38:07 -04:00
Robert Allan JamesandClaude Sonnet 5 f4615cf605 §H.12 step 18: zuse_eligibility.c -- read/add/membership-check
is_member() (fail-closed) and add() (idempotent, chains new devblocks
onto the tail as needed) over the item-17 eligibility-list devblock
chain, mirroring capsule_zuse_boot.c's magic/version/CRC-64 validation
convention. No callers yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 11:24:24 -04:00
Robert Allan JamesandClaude Sonnet 5 21ad5f7373 §H.12 step 17: zuse_eligibility_list.h -- eligibility-list record format
Growable owner_pubkey[32]-list devblock type for the metadata fence,
one slot over from zuse_genesis_marker_t (§H.5 Phase 6). Type only,
no read/add/check logic yet.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QgooKd5hJNtTYqB6CyK5f9
2026-09-03 11:12:37 -04:00
Robert Allan JamesandClaude Opus 5 46d89dd5be §H.12 step 16: block-acl.4th policy capsule -- Phase 5 (BMAPFMT) complete
capsules/block-acl.4th (blocks 4019-4020, next free range per
BLOCK_MAP.md): BLK-ACL-CHECK (block# -- allow?), a real fast-deny check
mirroring vm.c:611-624's pattern for blocks instead of words. First
touch lazily claims the block (allow=1, TTL=256, same base as ACL.4th's
own), matching the word card's default-permissive baseline. Not a stub --
genuinely does something on every call.

Loaded via a new EXEC line in init.4th right after ACL.4th's own;
confirmed ACL.4th's own activation is unaffected. Passed mkcapsule
--lint. Live-tested via QMP keystrokes: 1 BLK-ACL-CHECK executed cleanly.

Phase 5 (BMAPFMT) is now fully complete -- field layout, flags bits, C
accessors, FORTH wrappers, and a real policy word.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 07:44:28 -04:00
Robert Allan JamesandClaude Opus 5 405c713c4a §H.12 step 15: FORTH wrappers for BMAPFMT block-ACL fields
BLK-ACL-ALLOW@/!, BLK-ACL-TTL@/!, BLK-OWNER@ registered in block_words.c.
BLK-OWNER@ packs the 8-byte owner fingerprint into one cell (cell_t is
int64_t). No BLK-OWNER! -- ownership stays a controlled C-only operation.

Live-tested via QMP keystrokes on a running instance: 1 BLK-ACL-ALLOW@
executed cleanly against a real block. Verified 3-arch boot to ok>
(amd64/aarch64/riscv64) plus a hosted sanity build (shared source).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 07:36:57 -04:00
Robert Allan JamesandClaude Opus 5 15e6836ca3 §H.12 step 14: C accessors for BMAPFMT block-ACL fields
blk_owner_fp_get/_set, blk_acl_allow_get/_set, blk_acl_ttl_get/_set,
blk_flags_get/_set -- thin read-modify-write wrappers over the existing
blk_get_meta()/blk_set_meta() (caching/dirty-tracking already owned
there). Sets up the C-primitive layer FORTH wrappers (step 15) will call,
mirroring the word-level ACL system's own split.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 07:29:01 -04:00
Robert Allan JamesandClaude Opus 5 c19cc07ee3 §H.12 step 13: blk_meta_t flags bit constants
BLK_FLAG_CLAIMED/BLK_FLAG_MIGRATING/BLK_FLAG_STALE (bits 0/1/2), matching
the decided §F.4/§H.6 layout. Orthogonal bits, not a mutually-exclusive
enum.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 07:23:27 -04:00
Robert Allan JamesandClaude Opus 5 edd7effb5a §H.12 step 12: BMAPFMT field layout in blk_meta_t
Replaced the old 40-byte owner_id/permissions/acl_block/signature[2]
with owner_fp[8]/acl_allow/acl_ttl (u32)/acl_reserved[3]/reserved_future,
matching the decided §F.4/§H.6 layout.

Found a pre-existing bug via a real offsetof/sizeof compile check
(not hand math, per this step's own instruction): sizeof(blk_meta_t)
was already 344, not the 341 its own BLK_META_PER_BLOCK constant and
"341-byte slice" comment claimed -- harmless since that constant has
zero callers anywhere. New size after this edit's own alignment
padding is 336. Added a _Static_assert matching blk_volume_meta_t's
existing precedent, and fixed the stale comment to point at it.
BLK_META_PER_BLOCK itself untouched -- unused, out of scope.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 07:18:41 -04:00
Robert Allan JamesandClaude Opus 5 a268abe925 §H.12 steps 10-11: creator-ceiling enforcement, birth-time ACL snapshot
dictionary_snapshot_acl_from_parent(child, parent): walks the child's
dictionary, copies acl_allow/acl_mode/acl_pinned/acl_ttl from the
parent's matching word (by name, via vm_find_word() -- FIND's own
lookup, not modified) onto the child's entry. One-time snapshot at
birth, no live sync, matching H.3's decided rationale (a program
developed against one ACL set must not have it silently changed by
later parent changes).

Called once, after dict_hash/parity logging rather than before -- the
snapshot depends on the parent's current ACL state, which can vary
run-to-run once Zuse elevations exist, so applying it earlier would
break the "same capsule twice produces the same dict hash" determinism
invariant.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 06:44:58 -04:00
Robert Allan JamesandClaude Opus 5 ed86a759e1 §H.12 steps 7-9: thread real parent VMUuid through the birth call chain
Session.parent now comes from the actual birthing VM's own
stadium_vm_id, not a hardcoded vm_uuid_hera(). Added a VMUuid parent
parameter to capsule_birth_baby() and, one level up, to
capsule_console_birth()/capsule_runcap_birth() (neither had a VM* in
their own signature, but every caller did). Updated all 6 real call
sites: BIRTH, CAPSULE-BIRTH, CONNECT-ARTEMIS, CONNECT-HERMES,
RUNCAP-TEST, PAIR-TEST (mama_forth_words.c) and the console+user birth
pair in capsule_wirebind_try_attach() (capsule_wirebind.c). Two
functions had their vm parameter marked __attribute__((unused)), now
genuinely used -- attribute removed.

Steps 8 (Session.name from capsule name) and 9 (identity defaults to
installed=0) were already satisfied by step 5's existing
session_register() call and its identity-zeroing -- confirmed by
inspection, no further code needed.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 06:38:32 -04:00
Robert Allan JamesandClaude Opus 5 09998af999 §H.12 steps 5-6: pin Hera/Hermes/Artemis in generic capsule-birth admission
capsule_birth.c's generic admission block now registers a session for
every born VM (session_register) and pins it (session_set_pinned) when
the birthing capsule is Hera/Hermes/Artemis.

Bug found and fixed via a temporary probe (written, run, captured,
reverted): the fleet-foundation name check first used an exact-match
comparison against "Hermes"/"Artemis", but capsule_name is actually
"hermes:init.4th"/"artemis:init.4th" (the real namespace:filename
convention) -- the check silently never matched, both would-be-pinned
VMs stayed unpinned. Fixed with a new vm_name_prefix_eq_nocase() helper
matching everything before a literal ':'. Probe confirmed pinned=0 before
the fix, pinned=1 after, on all relevant VMs.

Session.parent is hardcoded to vm_uuid_hera() for now (every birth
through this path is Hera-initiated today); step 7 generalizes this to
the actual birthing VM's own id.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64) on the final,
probe-free code.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 06:26:50 -04:00
Robert Allan JamesandClaude Opus 5 67793ea4a1 §H.12 step 4: Hera registers as session zero; punch list to checkboxes
Rewired stadium_birth_hera() to admit unpinned then register through
session_register()/session_set_pinned() instead of setting
STADIUM_FLAG_PIN directly on the candidate header. Self-referential
parent (vm_uuid_hera(), vm_uuid_hera()), matching capsule_run.h's
parent_vm_id == vm_id root convention. Soft-fail, non-fatal, if
session_register() fails -- Hera's actual Stadium admission is what the
patron-zero invariant is about. Wired session_boot_init() into
kernel_main.c right after stadium_boot_init(), before stadium_birth_hera().

Also converted §H.12's punch list from bold "DONE" markers to this
document's established - [ ]/- [x] checkbox convention (already used
throughout §A), for consistency.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64), no soft-fail message
on any arch, Hermes/Artemis births unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 06:15:53 -04:00
Robert Allan JamesandClaude Opus 5 a621131ef6 §H.12 step 3: session_set_pinned/session_is_pinned pin-authority choke point
session_is_pinned() reads Session.pinned directly (authoritative, no
Stadium re-derivation); session_set_pinned() writes both Session.pinned
and the mirrored STADIUM_FLAG_PIN bit on the session's own patron cell,
keeping Stadium's internal eviction/admission logic (which must stay
self-contained) in sync without it calling back into session.c.

Added Session.stadium_cell (index into stadium_cells()) -- necessary
plumbing not in the original H.2 field list; the choke point can't reach
the right patron header without it. Moved STADIUM_FLAG_PIN from a
stadium.c-private #define to stadium.h (public) so session.c can
reference it without a duplicate definition.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 06:10:11 -04:00
Robert Allan JamesandClaude Opus 5 6d9fe3f515 §H.12 step 2: session-slot table, session_find/session_register
src/starkernel/vm/session.c: kmalloc'd-at-boot slot table sized from
stadium_max_vm_count() (mirrors stadium.c's own StadiumVMQuota, not a
fixed compile-time array as originally planned -- that table was already
moved off a fixed array for the same "population isn't knowable in
advance" reason). session_boot_init()/session_find()/session_register()
implemented for real, no stubs; session_register() zeroes identity and
leaves pinned=0, matching VMIdentity's own documented default and
deferring pin policy to callers. Added session.c to Makefile.starkernel's
explicit source lists. No callers yet.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 06:03:14 -04:00
Robert Allan JamesandClaude Opus 5 668e36bb17 §H.12 step 1: add Session struct (type only)
include/starkernel/session.h: vm_id (VMUuid), pinned (int, authoritative
over Stadium's STADIUM_FLAG_PIN per H.10), parent (VMUuid), name (fixed
64-byte buffer), identity (embedded VMIdentity, reusing the existing
type rather than inventing a new one). No logic yet, no callers -- next
step wires the session-slot array and register/find functions.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 05:57:01 -04:00
Robert Allan JamesandClaude Opus 5 4cd18734ee FABRIC-3.md §H.12: 22-item implementation punch list
One coding task per item across 7 phases, each gated by the mandatory
3-arch QEMU boot acceptance test. Two corrections found while grounding
this against live code: capsule_birth.c's generic admission path already
admits every VM as a Stadium patron (just hardcoded unpinned), so no new
stadium_birth_hermes()/_artemis() functions are needed -- the real task
is making that path pin Hera/Hermes/Artemis specifically. VMIdentity
(vm_identity.h) already exists fully built; Session.identity reuses it
directly rather than inventing a new type.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 05:50:00 -04:00
Robert Allan JamesandClaude Opus 5 89d814f9f3 FABRIC-3.md §H: close all three remaining design gaps
Gap #1: closed set of four cards (VM/word/block/message), no new cards
for now. Gap #2: creator-ceiling enforcement is a birth-time snapshot
copy, not a live ACL-RECHECK-time check -- reversed mid-conversation once
the live-check approach's cross-VM dictionary-lookup problem surfaced;
final rationale is child program stability (a program developed against
one ACL set must not have it silently changed by later parent changes).
Gap #3: Zuse's eligibility list persists in the existing growable
metadata-fence mechanism as a simple owner_pubkey[32] list, no extra
per-entry metadata.

Every design-level question in this refactor is now closed. What remains
is pure implementation (5 items) and two intentional deferrals.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 05:43:22 -04:00
Robert Allan JamesandClaude Opus 5 d86b6de9cd FABRIC-3.md §H.11: refreshed gap analysis, end of session
Full sweep across the whole session-refactor design pass -- 7 of the
original 10 §H.9 gaps are now closed. Compiles the current accurate
picture: one genuinely open design question (more cards beyond the four
named?), two small undecided pieces (creator-ceiling enforcement location,
Zuse eligibility-list storage), five pure implementation gaps (design
fully decided, nothing coded), and two explicitly-deferred non-goals
(VM card multi-owner, elevation trigger alternates). Also updates H.10's
stale "elevation trigger not yet usable" note now that H.7 confirmed the
messaging substrate is already real.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 19:04:48 -04:00
Robert Allan JamesandClaude Opus 5 91dfb0a079 FABRIC-3.md §H: close gap #8 -- EXPIRE/D.2 relationship reconciled
No actual conflict between D.2 and H.1. D.2 (2026-08-27, "session ending =
VM detach, reuse COOL") was scoped to user sessions, written before
Hera/Hermes/Artemis were framed as having their own persistent sessions.
D.2 is a special case of H.1's broader pinned/non-pinned model, fully
subsumed: it describes exactly how the non-pinned branch behaves. Pinned
sessions have no D.2-style "ending" at all -- permanent by construction.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 19:00:32 -04:00
Robert Allan JamesandClaude Opus 5 26b91f1ca4 FABRIC-3.md §H: close gap #7 -- private-topic traffic stays ungated
Checked MSG-SEND directly -- no ACL check anywhere in it today, on common
or a private topic alike. Confirmed: private-topic traffic stays ungated;
trust is established once at the handshake (CH-REQUEST/CH-ACCEPT/
CH-CONFIRM) and never re-checked per-message afterward. The message
card's scope is now final: it gates the single CH-REQUEST call, nothing
else in the messaging path.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:57:06 -04:00
Robert Allan JamesandClaude Opus 5 b15339ca1f FABRIC-3.md §H: close gap #6 -- CH is the topic, and H.7/H.8 are real code
Checked capsules/common/messaging.4th directly instead of assuming: the
H.7 messaging protocol isn't design vision, it's already substantially
built -- COMMON-CH, CH-REQUEST, CH-ACCEPT, CH-CONFIRM, CH-CLOSE, and
local ACK/NACK (MSG-ACK-LAST/MSG-NACK-LAST/MSG-NACKED) all exist and work
today. CH already is the topic, 1:1, no new representation needed.
Upgrades H.7/H.8's framing from "protocol groundwork, not yet real" to
"the mechanism exists, the ACL gate on top of it is the remaining work" --
the message card is now a concrete wiring task, not a hypothetical one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:55:09 -04:00
Robert Allan JamesandClaude Opus 5 33286cb3a2 FABRIC-3.md §H: close gap #3 -- word card IS the existing ACL system
Checked capsules/ACL.4th directly: both existing modes (STRICT, TTL)
already default to allow=1, matching the word card's default-permissive
baseline. Since each VM has its own dictionary, existing per-word ACL
state is already scoped per-session for free. Confirmed: the word card is
the existing acl_ttl/acl_allow/acl_mode/acl_pinned system reused as-is,
not a new parallel structure. Surfaces one new follow-on gap: creator-
ceiling enforcement at VM birth time isn't designed anywhere yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:52:32 -04:00
Robert Allan JamesandClaude Opus 5 fdecb91908 FABRIC-3.md §H: close gap #2 -- parent/name session fields confirmed
Both fields (parent VMUuid, name) confirmed as-is with no adjustment. All
five session fields (vm_id, pinned, parent, name, identity) are now
confirmed, none still model-proposed-only.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:50:23 -04:00
Robert Allan JamesandClaude Opus 5 de513483e3 FABRIC-3.md §H: close gap #1 -- non-pinned session mapping
Confirmed: an ephemeral user VM's session lands as a non-pinned,
COOL-subject Stadium patron -- synthesized from D.2's earlier finding
(session ending = VM detach, reuse COOL) rather than a fresh decision.
Thumbdrive detach is the explicit trigger. No exception case for a
temporarily-pinned user VM.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:48:25 -04:00
Robert Allan JamesandClaude Opus 5 197ff03963 FABRIC-3.md: BMAPFMT gets FORTH wrappers, design fully closed
Decided: yes, FORTH wrappers for the block-card ACL primitives, mirroring
the word-level ACL split (raw C accessors, policy composed in FORTH).
zuse_cert_seed's C-only precedent doesn't apply -- that's key material,
block ACL fields are ordinary ACL state like DictEntry's. BMAPFMT's
field/API design (owner_fp, acl_allow, acl_ttl, flags bits, FORTH
wrappers) is now fully decided across this and the prior three passes --
only the actual code edit remains.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:36:52 -04:00
Robert Allan JamesandClaude Opus 5 ce1618dcd8 FABRIC-3.md: BMAPFMT flags bit values decided -- CLAIMED/MIGRATING/STALE
blk_meta_t's existing unused flags field (uint64_t) uses orthogonal bits,
not a mutually-exclusive enum: bit 0 = CLAIMED, bit 1 = MIGRATING (serves
MIGSM), bit 2 = STALE (serves UNCLEAN), 61 bits reserved. BMAPFMT's field
design (owner_fp, acl_allow, acl_ttl, and now these flags bits) is fully
decided; only the actual code edit to block_subsystem.h/.c remains.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:29:05 -04:00
Robert Allan JamesandClaude Opus 5 a05e6d60ba FABRIC-3.md: BMAPFMT gets a TTL -- blocks support temporary elevation too
Resolves part of section F.4's deferred item #1 (allow-list/grant shape
beyond the single fast-deny bit): blk_meta_t's acl_reserved[7] becomes a
4-byte acl_ttl (mirroring DictEntry's countdown shape) plus 3 bytes still-
open slack, reusing the same ACL-TTL/Zuse-eligibility-list mechanism as
the word card. Kept lean -- no acl_mode/acl_pinned mirror, since a block
isn't pinned/strict the way a word is. Updated both F.4 (the field design)
and H.6 (the block card) to match.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:26:27 -04:00
Robert Allan JamesandClaude Opus 5 316de60671 FABRIC-3.md §H.10: close pin-authority choke point -- session owns both directions
Decided: session_set_pinned()/session_is_pinned() (or equivalent) are the
sole read AND write path for pin state -- nothing, including existing
Stadium code, touches STADIUM_FLAG_PIN on the patron header directly
anymore. Not just a write-side guard.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:22:43 -04:00
Robert Allan JamesandClaude Opus 5 a93d4fa2cd FABRIC-3.md §H.10: verify dictionaries are per-VM, not shared
Checked the load-bearing assumption underneath the word card's
creator-ceiling invariant and the H.5 elevation trigger, both of which
live on DictEntry ACL fields -- confirmed each VM gets its own separate
memory/dictionary buffer at birth (vm_bootstrap.c:181), so DictEntry ACL
state is already naturally scoped per-session. No conflict, no redesign.
Also notes two smaller, lower-risk open items: pin-authority sync between
Session and Stadium's flag, and the elevation trigger's dependency on
not-yet-real Hermes messaging.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:20:13 -04:00
Robert Allan JamesandClaude Opus 5 ba97623349 FABRIC-3.md §H: close gap #4 -- temporary Zuse elevation trigger decided
Trigger: live message-based request (ELEVATE-REQUEST on common or a
private topic, Zuse's session grants/NACKs, ACL-ALLOW!/ACL-TTL! write on
receipt), gated by a Zuse-held eligibility list keyed by owner_pubkey.
Grounded against capsules/ACL.4th directly -- ACL-TTL is a recheck-cadence
cache, not a grant mechanism; reuse means calling its ACL-ALLOW!/ACL-TTL!
primitives from new Zuse-triggered code, not new C primitives. Live-console
sudo-style grant and pre-signed capability tickets remain deferred, not
rejected, alternates.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:15:44 -04:00
Robert Allan JamesandClaude Opus 5 1349e783aa FABRIC-3.md §H: session refactor capture -- struct shape + identity/ACL cards
Design capture from today's session-refactor discussion, back to Hera:
session-as-Stadium-patron struct shape (references the patron by VMUuid,
pin-authoritative, identity embedded), and the identity ACL "stack of
cards" model with all four named dimensions scoped so far (VM, word,
block, message) plus the PubSub/topics messaging-protocol groundwork the
message card depends on. Closes with a numbered gap-analysis list (§H.9)
of everything still explicitly open.

Capture-only, matching this document's own established discipline -- no
struct written, no code changed. Mirrors §D's Tripod-vision capture in
style and structure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 18:08:35 -04:00
Robert Allan JamesandClaude Opus 5 f81e9c92bc Fix framebuffer console: scroll drift causes progressive line overlap in TTF mode
fb_scroll_rows() hardcoded the pixel distance it physically shifts the
framebuffer by as char_rows * 16 * scale -- the bitmap-font (font_8x16.c)
cell height -- regardless of which glyph mode vt100.c actually had active.
In TTF mode (the REPL's default, cell height 24px via VT100_TTF_CELL_H_PX)
this meant every scroll_up(1) call physically shifted the framebuffer by
only 16px while the text model (g_vt.rows, py_of()) placed each row 24px
apart. That 8px-per-scroll shortfall compounds with every subsequent
scroll: a few scrolls barely show it, but enough scrolls -- or scrolling
quickly, which is just many scrolls in a short span -- accumulates into
visible pixel overlap between rows, with newer lines drawn on top of the
tail end of older ones.

fb_scroll_rect() (the box-confined scroll added later for 4.4t) already
carried a doc comment calling this out explicitly, describing its own
explicit pixel_rows parameter as the fix for fb_scroll_rows()'s "fixed
16px-row assumption" -- fb_scroll_rows() itself was just never updated to
match.

Fixed by changing fb_scroll_rows()'s parameter from an implicit char_rows
count to an explicit pixel_rows count (matching fb_scroll_rect()'s
existing convention), and having its one caller (vt100.c's scroll_up())
pass lines * cell_h() -- the real active cell height -- instead of a raw
line count for the callee to guess at.

Verified: booted amd64 to the REPL (TTF mode active per sk_repl()'s own
console_fb_enable_ttf() call), let boot chatter + WORDS output scroll the
screen through thousands of accumulated scroll_up() calls, then measured
every visible line's y-position via a QMP screendump. Spacing held at a
perfectly consistent 24px (TTF cell height) top to bottom with zero drift
-- the old hardcoded-16px bug could not have produced that after this many
scrolls. Re-verified boot to ok> on all three architectures
(amd64/aarch64/riscv64) per repo acceptance policy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:25:06 -04:00
Robert Allan JamesandClaude Opus 5 c14324f498 Fix framebuffer console: idle heartbeat corrupts in-progress input line
sk_repl_idle() (called every ~1s from sk_console_readline()'s idle loop)
opens with console_ensure_line_start(), which unconditionally forces a
newline whenever the console isn't at a line boundary -- including
mid-edit, after characters have been typed and echoed but before Enter.
This fired on every elapsed SK_IDLE_BEAT_INTERVAL regardless of whether
sk_repl_idle() had anything to print, visually snapping the in-progress
input line to a fresh empty line -- indistinguishable from Enter having
been pressed. Most noticeable on the space key since it's the most common
key hit during a pause.

Gate the idle beat on n == 0 (no in-progress edit), mirroring the n > 0
guard the prompt reanchor logic just below already uses. Deferring the
xhci/block-sync idle service by at most one more interval while a line
is being edited is within its own documented "coarse cadence, cheap
early-exit" tolerance.

Verified: reproduced via QMP send-key against a live amd64 QEMU boot
(multi-character line typed with pauses across several idle intervals
stayed intact after the fix, where it previously broke on each interval).
Re-verified boot to ok> on all three architectures (amd64/aarch64/riscv64)
per repo acceptance policy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-02 17:12:21 -04:00
Robert Allan James daf9021ea5 Initial commit
Signed-off-by: Robert Allan James <robert.allan.james@gmail.com>
2026-09-01 12:09:20 -04:00
Robert Allan James 58c59e87e5 Initial commit
Signed-off-by: Robert Allan James <robert.allan.james@gmail.com>
2026-09-01 12:07:32 -04:00
Robert Allan James d2a0305703 Record driving deadlines for #1 (provisional expiry) and #2 (December deliverable)
Clarified 2026-08-29 with the author: "no hurry" is wrong for #1 and #2.

  - #1 (Hosted StarForth): the provisional will expire before conversion. Its
    priority claim is time-boxed (provisional filed ~Dec 2025, window ~Dec
    2026); if the non-provisional isn't filed claiming benefit before the
    window lapses the priority is lost and the same subject matter can't be
    re-staked by refiling thereafter. Hard clock regardless of December scope.
  - #2 (full StarshipOS): the December deliverable that keeps the flagship
    covered; pending counsel confirmation it may also be the conversion
    vehicle for #1's provisional.

Open point to resolve: is #2 (or the December delivery) the conversion vehicle
for #1, or is #1 converted by a filing separate from #2? Decision record only;
no filing made. Authoritative form in ROADMAP.md; FABRIC-3.md tracks it.
2026-08-29 10:36:19 -04:00
Robert Allan James 31dfc5de63 Record IP framing of the three products: 3 patent applications + 3 marks (StarForth, StarshipOS/LithosAnanke, Compudynamics)
The three-product split is treated as three patent applications, each matched
to a trademark (decided 2026-08-29):

  - Patent 1 + mark StarForth                  -> hosted/interpreted runtime
  - Patent 2 + marks StarshipOS (canonical spelling; noted as "StarshopOS"
    in the decision words) + LithosAnanke       -> full standalone OS
  - Patent 3 + mark Compudynamics               -> Zynq steady-state machinery
    with sealed executions, HOL-proven, anchored by the physics-adaptive runtime

Flagged open for counsel (not assumed/filed): the repo already carries a
"Patent pending" USPTO provisional filed Dec 2025 for the Compudynamics
physics-adaptive runtime (docs/patent/). Whether patent 3 is a continuation/
refinement of that provisional, and whether the three product applications are
additive to or fold it in, must be reconciled. Records intent only; no legal
text drafted or filed. Authoritative form in ROADMAP.md; FABRIC-3.md tracks it.
2026-08-29 10:32:04 -04:00
Robert Allan James 936d046ca6 Record the FPGA three-product split: Hosted StarForth / full StarshipOS / HOL-proven sealed-execution hardware
The Zynq FPGA is not a fourth platform to port to — it is the pivot that
forces the project into three distinct products, each with its own host,
delivery, and proof character (decided 2026-08-29):

  1. Hosted StarForth - the existing hosted/interpreted StarForth runtime
     (3-arch acceptance-tested), delivered as a portable embedded runtime.
  2. A full StarshipOS - the standalone OS built on LithosAnanke
     (LithosAnanke -> StarshipOS), a self-booting OS on general silicon
     (SER5/RasPi/Milk-V line).
  3. Hardware steady-state machinery with sealed executions, HOL-proven -
     the FPGA-native product: hardware-enforced sealed executions and
     steady-state machinery machine-checked in a proof assistant (HOL);
     delivery is the bitstream + HOL proof artifacts, not just an OS port.

The coupling is the point: product 3 is born on the FPGA, and its existence
is what cleanly separates 1 from 2 from 3. Product 1 ships hosted on an OS,
product 2 as an OS on general silicon, product 3 as proven hardware. Per-
product gates and the even-major line carrying the three as separate tracks
are scoped at v2.5.0 close / during the coloring-in phase. Complements, and
does not retract, the existing hardware bare-metal release policy.

Authoritative form in ROADMAP.md; FABRIC-3.md tracks the same horizon.
2026-08-29 10:30:09 -04:00
Robert Allan James 8e94522d10 Record trajectory beyond v2.5.0: Zynq FPGA next, after a "coloring in" hardening phase
The next big milestone after the three-board bare-metal cut (v2.5.0) is
transferring the battle-tested amd64/aarch64/riscv64 story to a Zynq (AMD
Xilinx) FPGA SoC — configurable silicon with soft/hard CPU cores, PL fabric,
and a non-standard memory map, a genuinely larger step than any prior board
(expected on a new even-major line). Between v2.5.0 and the Zynq sits a
"coloring in" period: hardening that thickens the shape of what exists rather
than adding silicon (USB BOT/xHCI + block robustness, live-entropy and
Zuse-cert hardening on real ASICs, SMP/multi-core + IRQ routing from the HAL
notes, driver breadth) so the FPGA carries a production-honest shape forward.

Authoritative form in ROADMAP.md "Beyond v2.5.0"; FABRIC-3.md tracks the same
horizon in the post-release section and re-points G.6's Next at the
v2.2.0/v2.4.0/v2.5.0 cadence.
2026-08-29 10:28:22 -04:00
Robert Allan James 25276ae359 Decide board-by-board hardware rollout: v2.2.0 (SER5 amd64) / v2.4.0 (RasPi5 aarch64) / v2.5.0 (all three)
Real silicon arrives incrementally (Beelink SER5 in hand now; RasPi 5 + Milk-V
orderable around Mon 2026-08-31), so the hardware release is split per board
in hand, each an even-minor LTS point-in-time cut on the same line:

  - v2.2.0  amd64 bare metal  — Beelink SER5 (two 16GB sticks: one boots the
                                thumbdrive image, one mints a real Zuse user)
  - v2.4.0  aarch64 bare metal — Raspberry Pi 5
  - v2.5.0  all three bare     — adds Milk-V (riscv64)

Each closes only when its board's items are proven live (boot to ok>, block-
fence Zuse true, backend entropy non-deterministic), never build-only.

Records the decision in the authoritative Release Versioning Policy
(docs/lithosananke/ROADMAP.md, "Board-by-board hardware rollout, decided
2026-08-29") and re-maps G.4/G.5 in FABRIC-3.md onto the new cadence, noting
v2.0.0 was cut (tag v2.0.0).
2026-08-29 10:22:36 -04:00
Robert Allan James 28de700645 v2.0.1: G.4 amd64 RDRAND backend behind rng_get_bytes() (SER5 entropy)
First real per-arch RNG backend, added to the v2.0.0 unified entry point in
src/starkernel/rng/rng.c, #if-guarded to amd64: CPUID.01H:ECX[30] RDRAND
detection + inline-asm rdrand draws feeding rdrand_fill() (whole-byte
emission from the low end; a partial final draw is discarded -- throwing
away entropy is always safe).

Probe order honors the release policy: virtio-rng is tried first, so the
QEMU path stays on virtio-rng unchanged; RDRAND is the fallback only real
hardware (which has no virtio-rng device) reaches. QEMU-verified both ways
on amd64: with virtio-rng present -> "rng: backend = virtio-rng" (unchanged);
with virtio-rng absent and RDRAND exposed (-cpu max) -> "rng: backend =
rdrand" + "entropy: ready" + Zuse attach confirmed. rdrand_fill()'s exact
logic host-proven: fills 32-byte/16-byte buffers and yields differing draws
run-to-run (non-deterministic). aarch64/riscv64 builds unaffected (guarded
off). riscv64 Zkr and aarch64 peripheral-RNG backends remain parked for their
real boards.

FABRIC-3.md G.4 amd64 slice marked BUILT + QEMU-verified.
2026-08-29 10:18:07 -04:00
Robert Allan James 19bc90c97d v2.0.1: document G.6 generic thumbdrive image milestone in FABRIC-3.md 2026-08-29 10:12:58 -04:00
Robert Allan James bd266942ce v2.0.1: generic GPT/FAT32 UEFI bootable thumbdrive image (thumbdrive goal)
New `make -f Makefile.starkernel thumbdrive` goal builds a generic
UEFI-bootable GPT disk image (disk image -> GPT + one FAT32 "STARKERNEL"
EFI System partition with EFI/BOOT/BOOT<ARCH>.EFI + startup.nsh) that can be
written directly to a USB thumbdrive with dd and boots on any real amd64 UEFI
firmware (Beelink SER5 path) as well as under QEMU. The monolithic loader
embeds the whole kernel, so the ESP needs only the UEFI fallback boot path.

Verified under QEMU by attaching the image as a USB mass-storage device (not
cdrom): OVMF BDS auto-selected "UEFI QEMU QEMU USB HARDDRIVE" (Boot0002),
then kernel booted normally -- LithosAnanke v2.0.0, POST 1012/0/0 + ok>,
rng: backend = virtio-rng + entropy: ready, Artemis ready, and the USB BOT/
xHCI storage path enumerated (READ CAPACITY10 -> MSC device ready -> READ10
CSW PASS). This mirrors the real-hardware SER5 flow: firmware boots the USB
thumbdrive's ESP, and the OS's own storage rides the same USB BOT/xHCI path.

Works for all three arches via the arch-mapped EFI boot name.
2026-08-29 10:12:43 -04:00
Robert Allan James 2efd7fe7e7 v2.0.0: QEMU release — bump LITHOS_VERSION to 2.0.0
The §G v2.0.0 QEMU release-gate punch list is complete (G.1 xHCI stall
recovery, G.2 unified rng_get_bytes() entropy entry point, G.3 NVRAM
de-scoped). Bump the kernel version to 2.0.0 (even major = LTS, per the
Release Versioning Policy: X.0.0 = QEMU release, X.5.0 = hardware bare-metal
release). Update README and .claude/CLAUDE.md version references and the
Makefile.starkernel version-roadmap comment.

Verified: all three arches (amd64/aarch64/riscv64) build clean with v2.0.0
embedded; QEMU amd64 boot shows "LithosAnanke v2.0.0", POST 1012/0/0 + ok>,
rng: backend = virtio-rng + entropy: ready, and Zuse identity confirmed from
the attached thumbdrive.
2026-08-29 10:09:37 -04:00
Robert Allan James 09857b7228 G.2 (v2.0.0): unified rng_get_bytes() entropy entry point; virtio-rng sole backend
The QEMU-verifiable slice of the real-hardware RNG driver (per FABRIC-3.md
§G.2). New include/starkernel/rng.h + src/starkernel/rng/rng.c provide the
single entropy entry point: rng_init() probes the backend set (v2.0.0:
virtio-rng only) and, on no backend, prints a loud boot-time warning while
rng_get_bytes() returns RNG_ERR_NO_BACKEND - never silently degrading to a
deterministic seed. The backend-selection switch in rng.c is the exact seam
v2.5.0's per-arch drivers (amd64 RDRAND, riscv64 Zkr, aarch64 peripheral) plug
into without touching the call path.

Consumers route through the unified layer instead of virtio-rng directly:
capsule_mint.c (identity seed + drive_uuid) and kernel_main.c phase 8
(rng_init()). virtio_rng.c stays as the sole backend. Built clean on
amd64/aarch64/riscv64. QEMU amd64 boot: POST 1012/0/0 + ok>, "rng: backend =
virtio-rng" + "entropy: ready", Zuse identity confirmed from thumbdrive -
mint/cert behavior unchanged.

FABRIC-3.md §G.2 v2.0.0 slice marked BUILT+VERIFIED.
2026-08-29 09:57:17 -04:00
Robert Allan James aee1ecaa8f G.4 (2h): fix dangling §G.4 reference in G.1 follow-up note 2026-08-29 09:50:06 -04:00
Robert Allan James dc2f38a1e1 G.4 (2h): bounded xHCI event-ring drain fixes boot-attach livelock
Root cause of the G.1 follow-up boot-time attach race: on pathological
controller behavior the xhci_poll_events() drain loop had no hard ceiling.
ERDP is written back only when the loop exits, so the controller cannot
reclaim event TRBs mid-drain; if it keeps producing events the head can
chase the software dequeue pointer forever. xhci_poll_events() never returns,
sk_repl_idle() never reaches its bot_msc_attach_pending check, and a fresh
USB BOT device that finished SET_CONFIGURATION is left flagged-but-never-
attached while the guest appears hung.

Fix: bound the drain to a full ring (XHCI_EVT_RING_MAX_DRAIN = 256), so
xhci_poll_events() always terminates and always writes ERDP each call.
Unprocessed events keep their cycle bit and are re-read next poll; nothing
is dropped. On the healthy path one drain processes only the one-or-few
events the controller posts per chained command, so the bound never triggers
except in the pathological case it breaks.

Beyond the G.1 additions: a new macro in include/starkernel/xhci.h and a
bounded loop in src/starkernel/usb/xhci.c. Builds clean on amd64. Verified
across six consecutive fresh QEMU boots (previously intermittently hung).
2026-08-29 09:48:48 -04:00
Robert Allan James 49a3faa331 G.1: xHCI bulk-endpoint stall recovery (per F.14), built + verified
Full BOT-spec stall recovery per FABRIC-3.md F.14: new STALL_ERROR handling,
Reset Endpoint + Set TR Dequeue Pointer commands, CLEAR_FEATURE(ENDPOINT_HALT),
escalating to Bulk-Only Mass Storage Reset, capped retries
(XHCI_BOT_STALL_MAX_RECOVERIES=2) mirroring bot_tur_retries, clean terminal
failure via xhci_stall_fail().

Purely additive recovery path off the non-success transfer-event branch; the
normal path is unchanged. Builds clean on amd64/aarch64/riscv64. QEMU amd64
boot regression passes: zero stalls, BOT attach (READ CAPACITY10 -> READ10 ->
home-blocks) completes, normal-path xHCI trace identical to baseline. Live
stall injection is not provable under qemu-xhci; deferred to v2.5.0 hardware.

FABRIC-3.md G.1 documented; ROADMAP release-versioning policy folded in.
2026-08-29 00:58:59 -04:00
Robert Allan James 5689c397fc Bug-fix sweep: repl reentrancy, virtio/blocksys bounds, identity CRCs, LOG_LINE_MAX
Code review fixes, all compile clean (hosted gcc + aarch64/riscv64 kernel flags):

- repl.c (H1): reentrancy guards on the MSG-TICK idle pump. sk_repl_idle()
  now defers when Hera is mid-interpret (g_mama_interpreting) or when its
  own vm_interpret is on the stack (g_idle_pump_active), so a blocking
  KEY/EXPECT/QUERY inside a dispatched line can no longer re-enter the
  interpreter and clobber the in-flight input buffer.
- virtio_rng.c: clamp device-returned used_len to VRNG_BUF_SIZE before the
  caller's data_buf copy, closing a device-controlled OOB read.
- block_subsystem.c: first-write path now keys off created_time==0 instead
  of dead magic==0 so fresh blocks get a real created_time stamp; first_free/
  last_allocated fixed to absolute Forth LBNs (set in blk_compute_fresh_geometry
  from slot->start_lbn, no longer the wrong physical-BAM-index values from
  compute_totals_from_B); physical-bounds guard on blk_meta_zone_read/write
  prevents unsigned underflow on a corrupt fence >= device size.
- capsule_zuse_boot.c / capsule_wirebind.c: identity seed validated magic ->
  version -> CRC-64 (compute_crc64 over offsetof(crc)) before trusting it,
  so a corrupt/format-mismatched record is refused, never loaded.
- log.h / starkernel/log.h: unused LOG_LINE_MAX 256 renamed LOG_MSG_LINE_MAX
  to lift the include-order collision with vm.h's LOG_LINE_MAX 64; stale
  include-order comments dropped (kernel_main.c, shim.c, capsule_birth.c).
- FABRIC-3.md: three stale-doc carry-forward items closed [x] with cbe7b49
  notes.

Real KEY/?TERMINAL/QUERY/EXPECT bodies (console WIP):
- repl.h/repl.c: sk_console_getkey()/sk_console_key_available()/
  sk_console_readline() public bodies; non-destructive peek buffers the
  found byte so a following KEY returns it.
- shim.c: getchar()/fgetc()/fgets()/sf_terminal_ready() routed through the
  real console paths instead of stubs; sf_terminal_ready() in platform_io.h
  with sf_terminal_ready() implemented for the hosted build (linux/io.c,
  POSIX select on fd 0) wired into Makefile.
- io_words.c: ?TERMINAL now returns actual terminal-readiness, not constant 0.

Artifacts: minted disk/artemis.img + rebuilt lfs kernel; BLOCK_MAP.md,
doe csv + qemu log regenerated.
2026-08-28 23:28:10 -04:00
Robert Allan JamesandClaude Sonnet 5 a54e84b2d6 FABRIC-3.md: close §F.27, first real second identity minted, MSG-TICK gap found+fixed
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBjfeLPo71sUQ8zC7V7P5m
2026-08-28 22:06:00 -04:00
Robert Allan JamesandClaude Sonnet 5 d00e6cb50d MINT: default personality now loads common:messaging.4th (fixes MSG-TICK error-spam)
capsule_mint_identity()'s MINT_DEFAULT_PERSONALITY only ever defined
WELCOME, unlike capsule_console.c's own CONSOLE_IDENTITY_SRC. The moment
WIREBIND made a freshly-minted identity's VM live, Hera's per-idle-tick
MSG-TICK pump (repl.c Phase C) began erroring on it every tick forever --
UNKNOWN WORD: 'MSG-TICK'. Every identity minted before this fix would hit
the same infinite error-spam on going live, not just this one.

Fixed by loading common:messaging.4th + calling MSG-CD-INIT before
WELCOME, mirroring capsule_console.c's own pattern exactly.

Found and verified minting the first real second identity end to end:
disk/captain-bob.img, hot-attached via QMP alongside an already-
authenticated Zuse session, MINTed, WIREBIND-attached, switched into
live with USE, WELCOME confirmed. Zero MSG-TICK errors over 18+ seconds
of idle-loop cycling after the fix (was immediate and continuous before).
Full three-architecture regression clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBjfeLPo71sUQ8zC7V7P5m
2026-08-28 22:05:55 -04:00
Robert Allan JamesandClaude Sonnet 5 0db894d9c7 FABRIC-3.md: close §F.25 default-attach directive (§F.26)
Documents the mismatched-marker/blank-drive root cause, the LOG_INFO-vs-
LOG_WARN filtering trap that made the debug probes look like they weren't
firing, the three-architecture acceptance results, and the answered "why
not just reboot after MINT" design question (both branches already share
install_and_activate(), so there's no duplicate path to reconcile).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBjfeLPo71sUQ8zC7V7P5m
2026-08-28 21:42:21 -04:00
Robert Allan JamesandClaude Sonnet 5 849b83b727 Zuse default-attach: xHCI initial-port-scan fix, ZUSEDISK wiring, mismatched-marker resync
xhci_bringup() now scans for already-connected ports at bring-up
(xhci_scan_ports_for_already_connected()), not just later hotplug events,
so a USB device present on the QEMU command line at launch is detected.
Makefile.starkernel attaches disk/zuse.img on the xhci0 bus by default in
all three arch qemu targets (ZUSEDISK=, empties for a bare boot).

capsule_mint_identity() gained a drive_known_blank param to skip a fully
redundant second homeblocks_sig_check() when the caller already confirmed
HOMEBLOCKS_SIG_BLANK itself.

Root-caused what looked like a hang after the drive attached: Artemis's
fence still carried a genesis marker from before a mid-session reformat,
while the reformatted disk/zuse.img read back BLANK -- a mismatched pair
capsule_zuse_boot_try_attach() correctly declined to act on, leaving the
boot idling at a plain ok> with nothing left to log (indistinguishable
from a hang under slow TCG). Fixed by zeroing both disk/artemis.img and
disk/zuse.img at their original sizes, giving a matched blank pair.
Verified full three-architecture acceptance: amd64 fresh genesis-mint,
aarch64/riscv64 clean reload against the same now-minted images.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBjfeLPo71sUQ8zC7V7P5m
2026-08-28 21:42:14 -04:00
Robert Allan JamesandClaude Sonnet 5 3f74ff0f78 FABRIC-3.md: close BINDSTEP + fence-persistence, identity arc done (§F.24)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
2026-08-28 18:29:26 -04:00
Robert Allan JamesandClaude Sonnet 5 09d78c99d0 BINDSTEP + fence-persistence fix: identity arc closed end to end
Two items, closed together per direct instruction.

1. Fence-persistence root cause, found and fixed: meta_fence_blocks
   (the field gating whether blk_meta_zone_write() can succeed at all)
   was carved out of what used to be unused padding in blk_volume_meta_t
   -- the code's own comment already documented this. disk/artemis.img
   was formatted before that field existed, so its on-disk bytes there
   have always read back as 0, and the existing-volume load path
   (blk_format_or_load_disk()) never recomputes it -- only a fresh
   format does. Every "fence write FAILED" message this entire session,
   old block-fence flow and new zuse_genesis_marker_t alike, traces to
   this one thing. Patching the field in place without redoing the rest
   of the geometry would risk corrupting whatever's already allocated
   near the top of the volume, so the only safe fix is a genuine
   reformat -- done, with explicit confirmation, since it discards
   disk/artemis.img's accumulated persistent test state (regenerated
   fresh at next boot regardless, not real data). Verified: fence write
   now succeeds with no failure suffix, and the full mint-once ->
   reboot -> reattach -> re-authenticate cycle works for the first time
   this session ("Zuse: identity confirmed from attached thumbdrive",
   ZUSE-SESSION? goes 0 -> -1 without re-minting).

2. BINDSTEP (FABRIC-3.md §F.9): capsule_wirebind_verify_cert() extracted
   as a shared function so WIREBIND (the original attach) and BINDSTEP
   (every USE of an identity-locked VM) check the exact same thing the
   exact same way. mama_word_use() now re-verifies live, not cached,
   whenever the target VM has VMIdentity.installed=1 -- reads whatever
   drive is CURRENTLY attached, re-verifies its cert, compares owner
   pubkey against the target's own installed identity, refuses on any
   mismatch or no drive attached. A target with installed=0 (Hera,
   Hermes, Artemis, any console VM) stays freely targetable, unchanged.

Two related bugs found and fixed live while testing BINDSTEP, not
assumed away: USE was Mama-only, so a console-paired session (§F.22)
had no way back to Hera at all -- any attempt to call USE from inside
a console VM hit "UNKNOWN WORD: USE", a genuine dead end. Per direct
instruction, USE isn't console-specific -- it should work VM-to-VM
universally, same as VM-EXEC already does -- so it's now registered in
register_child_vm_words() too. That alone wasn't enough: the console
relay (sk_repl_dispatch_line()) would have captured a bare USE call and
sent it to the paired user VM as a message instead of running it.
Fixed with a small suffix-match guard (sk_repl_line_calls_use()) --
real FORTH syntax always puts USE last, so a trailing-token check
reliably recognizes it without needing a full tokenizer, and it always
runs directly, never relayed.

Verified live end to end: USE on an unlocked VM works unconditionally;
USE escaping a console back to Hera now works; USE on an identity-
locked VM succeeds while its own drive is attached and is refused
once detached ("USE: FinT~user refused -- no matching identity
currently attached"). Clean 3-architecture regression, including
confirming disk/artemis.img's reformatted geometry loads correctly as
an already-recognized volume ("Artemis: LithosAnanke disk -- resuming")
on aarch64 and riscv64 too, not just the amd64 boot it was reformatted
under.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
2026-08-28 18:28:53 -04:00
Robert Allan JamesandClaude Sonnet 5 9ee029e7d4 FABRIC-3.md: close WIREBIND, the real attach call site (§F.23)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
2026-08-28 16:57:45 -04:00
Robert Allan JamesandClaude Sonnet 5 6fc0ee33a9 WIREBIND: real thumbdrive-attach call site, no manual steps
Assembles pieces already built and individually verified this session
-- CERTVERIFY (vm_identity_from_cert(), Phase A/B), RUNCAP, the
console-VM + user-VM pair (§F.22) -- into one automatic sequence,
replacing the RUNCAP-TEST/PAIR-TEST diagnostic words that exercised
each piece by hand.

New capsule_wirebind_try_attach() (capsule_wirebind.h/.c), called from
sk_repl_idle() alongside capsule_zuse_boot_try_attach() on every
HOMEBLOCKS_SIG_OK attach: sig->cert_offset==0 means this is Zuse's own
genesis-mode drive (no cert region) -- that's already
capsule_zuse_boot_try_attach()'s job, skip. Otherwise, with Zuse already
authenticated this boot (nothing to verify a regular cert against
otherwise), reads the cert devblock(s) and calls vm_identity_from_cert()
against mama_vm's own zuse_cert_pubkey and the drive's own drive_uuid.
On success: reads the drive's own user_identity_seed_t for its
username, births a console VM + RUNCAP-born user VM pair (idempotent --
no-ops if that username is already live this session), installs the
verified VMIdentity onto the user VM, and registers the "<username>~user"
pairing sk_repl_dispatch_line() (repl.c, §F.22) looks for. Deliberately
does NOT auto-USE the new console -- that stays an explicit,
ACL-gated step (BINDSTEP, §F.9), not something a bare attach should
trigger silently.

Verified end-to-end live in QEMU, including a genuine negative case:
attached disk/user1.img (signed by a different, earlier-session Zuse
instance) and got a correct "cert verification FAILED -- drive
refused" -- proof the check is real, not a rubber stamp. Minted a
fresh identity with this boot's own Zuse, reattached, and got
"WIREBIND: SamS attached and ready" printed with zero manual commands,
followed by a working USE + async WELCOME relay end to end (queued,
no UNKNOWN WORD, delivered and executed in the paired user VM on the
next idle tick). Clean 3-architecture regression: Hermes/Artemis both
birth live, no unexpected ACL denials or UNKNOWN WORD.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
2026-08-28 16:57:17 -04:00
Robert Allan JamesandClaude Sonnet 5 c9cf9b09d9 FABRIC-3.md: close console-VM + user-VM async relay (§F.22)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
2026-08-28 16:39:43 -04:00
Robert Allan JamesandClaude Sonnet 5 b0f12710bb Console-VM + user-VM pair: real async message-passing relay
Console sessions now route through the same general VM-to-VM messaging
system (Phase C) any VM can already use for its own reasons -- not a
synchronous shortcut. Per direct instruction: real async MSG-SEND/
MSG-DELIVER (Option B), not a VM-EXEC-based synchronous relay, because
messaging is a general capability, not a console-specific mechanism.

New CONSOLE-CMD-EVENT message type (common:messaging.4th). New
sk_repl_dispatch_line() (repl.c), called from both sk_repl_step and
sk_repl_run in place of a direct vm_interpret(): if the active VM's own
name has a live "<name>~user" counterpart registered, the raw input
line is wrapped as an S"-embedded CONSOLE-CMD-EVENT MSG-SEND and
interpreted on the console VM instead of being run directly -- the
console's own next MSG-TICK (Hera's idle pump) delivers it into the
paired user VM via VM-EXEC, same mechanism every other message already
uses. Falls back to direct interpretation if there's no pairing, or if
the line contains a `"` (known v1 limitation, warned about explicitly
rather than silently mishandled).

New capsule_console_birth() (capsule_console.h/.c): a bare VM whose
only content is loading common:messaging.4th -- the console side of a
pairing, parallel in shape to RUNCAP's user-VM birth but with fixed
embedded content instead of a devblock read (no identity, no thumbdrive
involved). New PAIR-TEST diagnostic word (mama_forth_words.c, matches
RUNCAP-TEST's own precedent): births both halves of a pairing and
registers the "<name>~user" mapping. Not the real pairing call site --
that's the eventual attach/onboarding flow -- this exists to exercise
the relay live before that flow exists.

Found and fixed a real, serious bug live: console_set_vm_name() stored
the caller's raw pointer instead of copying it. mama_word_use() (USE)
passes a VMRegistryEntry field living on its own stack frame -- once
USE returns, that pointer dangles, corrupting every console tag after
the first USE (observed directly as garbled "[[]" / binary-looking
prefixes instead of "[CaptBob]"). Fixed at the source: console_set_
vm_name() now copies into internal storage. That surfaced a second,
related bug across every console_get_vm_name()-based save/restore call
site in mama_forth_words.c (BIRTH, VM-STEP, VM-EXEC, CONNECT-HERMES,
CONNECT-ARTEMIS): saving just a pointer into the single internal buffer
meant an intervening console_set_vm_name() call silently corrupted the
saved value before the restore ever ran. New console_save_vm_name()
copies into caller-owned storage; every save/restore site updated.

Verified end-to-end, live in QEMU: typed WELCOME at a paired console
VM -- it did not execute directly (no UNKNOWN WORD), printed ok
immediately (queued, async), and on the next idle tick
"[CaptBob~user] Minted identity -- default personality" appeared on
its own -- genuine delivery and execution in the paired user VM through
the real MSG-SEND/MSG-DELIVER pipeline. Console tags confirmed clean
(no garbling) across all three architectures' full regression boot.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
2026-08-28 16:39:07 -04:00
Robert Allan JamesandClaude Sonnet 5 71b6937deb FABRIC-3.md: close emergency-CLI retirement + thumbdrive Zuse (§F.21)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
2026-08-28 16:11:23 -04:00
Robert Allan JamesandClaude Sonnet 5 cc9521d2cc Retire emergency CLI: Zuse goes thumbdrive-resident, ACL.4th activated
Three tightly-coupled changes, verified together per Captain Bob's own
"getting rid of the emergency cli" direction:

1. Zuse's identity is thumbdrive-resident, never system-resident. New
   zuse_genesis_marker_t (magic/version/zuse_pubkey[32]/crc) replaces
   zuse_cert_devblock_t's slot in the top-of-device fence -- the system
   now remembers only that a root identity exists and its pubkey, never
   a seed. zuse_cert_devblock_t is kept in the repo, marked superseded,
   no longer written by any code path.

   capsule_mint_identity() grows a genesis mode (issuer_vm=NULL): no
   cert is built or written (Zuse isn't verified against a separate
   signer -- she's recognized by pubkey match against the marker) and
   two new optional out-params (out_pubkey/out_seed) let the caller
   install the cert immediately after a genesis mint.

   New capsule_zuse_boot_try_attach() (capsule_zuse_boot.c), called
   from sk_repl_idle() on every fresh USB attach (the only point in the
   boot lifecycle a thumbdrive can actually be detected -- attach
   polling doesn't exist yet at kernel_main.c's old one-shot mint point,
   which is why that whole block is gone): no marker + blank drive ->
   genesis-mint; marker present + matching drive -> read its own
   user_identity_seed_t, install the cert. Either way, re-runs
   ACL-ZUSE-BOOT (zuse.4th) so zuse_session activates exactly like it
   always has for a same-boot cert install -- ACL-PIN only blocks
   redefinition, not re-execution, so no new C-side auth logic needed.

2. ACL.4th activated (capsules/init.4th) -- inactive all session until
   now. Found and fixed a real bug this immediately surfaced: zuse.4th's
   ACL-ZUSE-BOOT tried `['] ACL-ZUSE-BOOT ACL-PIN` from inside its own
   still-compiling definition -- the word isn't findable yet at that
   point, so the whole definition silently failed to compile every
   previous boot this session (dormant, since ACL.4th never loaded).
   Fixed: pin after the definition closes, not from within it -- it
   only needs to happen once anyway, and pinning doesn't block the
   re-invocation genesis/attach needs.

3. The unauthenticated emergency-CLI ACL bypass is retired
   (repl.c): `emergency_console = is_hera ? (zuse_session ? 0 : 1) : 0`
   deleted from both sk_repl_step and sk_repl_run. Every word run from
   Hera's own bare prompt now goes through ordinary ACL enforcement;
   emergency_console is driven only by the genuine C-level fault
   handler again.

Added ZUSE-SESSION? (starforth_words.c), a read-only diagnostic
matching ZUSE-PUBKEY@'s own precedent, to verify the whole chain
directly rather than by inference.

Verified end-to-end live in QEMU: fresh boot, no thumbdrive ->
ZUSE-SESSION? reads 0. Attach a genuinely blank drive via QMP -> genesis
mint fires automatically (no typing) -> ZUSE-SESSION? reads -1 (true).
Hermes/Artemis both birth clean on all three architectures with ACL
now actually enforced for the first time all session -- no denials, no
UNKNOWN WORD beyond the deliberate POST self-test cases.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019ZGkimpfyh63EZyRkNbkPD
2026-08-28 16:10:40 -04:00