Any node may birth and wire its own; only Hera sleeps, wakes and kills, by
number. Requests have fixed numbers, -6 to -18. NODE-BORN takes the
number; NODE-KILL a number. test_manage.c: 41 checks at both widths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixed request numbers; NODE-BORN takes the number; GONE handed on through
the system's (GONE-HOOK), so dict_hash changes; a reader per asker.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ruled 2026-10-16: Hera births the second centre and it births its own unit;
hera.4th split into centre.4th (BIRTH-AS, BIRTH, UNIT) and Hera's own
(KILL, PAUSE, RESUME, UNIT-KILL, CENTRE); the kernel keeps the registry and
checks who may ask; centres face each other on port 6; GONE goes down the
birth graph.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MESH.md ruling 7 and section 9 said only Hera's requests are honoured.
Ruled 2026-10-16: v4 births behave as v3's (any VM may BIRTH, the child's
parent is its birther, its ACLs are copied from the parent's at birth,
KILL is Hera's alone).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DOE only types its name for now, as asked 2026-10-15; the runner is still to be
built. Not yet a system capsule (MACHINE.md 6, step 3). With it, the two board
boots, amd64-pc and aarch64-rpi5, that tried it: DOE is unknown until
STARSHIPOS is in the search order.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
amd64, aarch64, riscv64: ok>, POST 538/538, dict_hash=0xfec8c47cfc6f437f,
rom_hash=0x87b23d3ae2643c8d (2372 words) on all three; Q./ answers 0 21845.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DATASHEET-F18.md lists all 19 entries of the host ROM, the six shared ones
with the registers they use. MACHINE.md 2.2: built; 277 words, not the
estimated 150-200; the system's words are address, push and ;.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MACHINE.md 2.2. Hera carries the host's ROM; the four outer nodes the
shared math only, and compute UM*, U/MOD and Q./ with it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MACHINE.md 2.2. Each is its entry's address, push and ;. Every node the
tests make carries the host ROM (host_load, Hera's births, the depth and
mesh tests' own nodes), and a division by zero inside the ROM returns to
the prompt (hosted-check). test_host_quit's fetch outside memory moves
past the ROM's range: 16384 is the ROM's first word now the tests' system
has its ROM, as the product's always had. dict_hash 0xfec8c47cfc6f437f.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MACHINE.md 2.2. UM*, UM/MOD, D+, DNEGATE, Q.* and Q./ are entries 0-5
of the host ROM and of a mesh node's ROM, from one source (rom/math.f18)
and one entry list (v4/rom_entries.h). The host ROM starts at entry 18.
The mesh ROM is 277 words; the host ROM 2372.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Shared math in a small ROM for mesh nodes: UM*, UM/MOD, D+, DNEGATE,
Q.* and Q./ as entries 0-5 of both ROMs, from one source; the host
ROM's assembler entries follow, and its start moves from 12 to 18.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Text after start is refused, as text.c refuses it: before it jumps, the
ROM reads on to the end of the source; blanks, comments and the ends of
blobs may follow, a token is ERR-AFTER (26), "text after start".
- A token too long where a name, a target or a value is wanted is "name
too long", as text.c says: (MISSING) reports "missing" only when no error
came first. `", keeps "missing", which is text.c's word for both.
- text.c's "address out of range" names the address, not the value.
- The system's source files are one list, v4/sys_files.h: boot.c, the
harness and the nucleus test are made from it.
- The ROM's error codes and words are one list, v4/rom_errors.h: boot.c's
messages, host_map.h's ROM_ERR_* and mkmap's ERR-* are made from it.
- node.h says what v4_node_store does with a ROM address (nothing, as for
any address outside RAM, D-14); test_host_rom_range pins it.
ROM: 2095 words, rom_hash 0x06d0dcb7a0cb41a1. dict_hash unchanged,
0xe5a57f3eb72ff94a. make -C v4 test test-32 hosted-check pass. QEMU:
amd64, aarch64, riscv64 to ok>, POST 538/538, POST: PASSED.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The boot left the source's last new line in the console input, and the
first KEY after the boot read it. What the ROM has not read when it
starts the system is taken back (boot.c, rom_harness.h). hosted-check:
KEY after the boot reads the key typed.
- A comment or macro left open at the end of a blob ran on into the next,
and the ROM accepted source text.c refuses. The loader ends each blob
with FS (V4_ROM_BLOB_END) after its new line; the ROM ends a blob there
as text.c ends a file: an open comment or macro is an error, and the word
being assembled ends. test_host_rom_asm: seven blob pairs against text.c.
- store to an address outside RAM faulted the ROM with no message. It is
now refused, as text.c refuses it: ERR-RANGE, "address out of range".
- The kernel build did not regenerate capsules/v4/sys/map-<w>.f18 before
packing the capsule directory; it now does.
ROM: 2084 words, rom_hash 0x4c458c6e48b35456. dict_hash unchanged,
0xe5a57f3eb72ff94a. make -C v4 test test-32 hosted-check pass. QEMU:
amd64, aarch64, riscv64 to ok>, POST 538/538, POST: PASSED.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The host node starts in its ROM (entry 12), and the boot feeds it the
system's source through the console: capsules/v4/sys/map-64.f18, the
fourteen .f18 blobs and end.f18, each found, hash-checked and
signature-checked as any capsule, then EOT. The ROM assembles the system
and starts it at (IDLE); the host reads the image record
(v4_image_from_node) for what only the assembled system knows. A ROM
error is reported with its blob, line and meaning, then PARITY:FAIL.
Deleted: v4/tools/mkimage.c, capsules/v4/nucleus-64.f18, v4/capsule/*.v4,
v4/tools/v4tof18.py. The .f18 files are the source now.
- text.c speaks the rest of the ROM's language (set, here, store, start;
v4_text_value, v4_text_start), so the oracle reads the same source:
test_host_rom_nucleus compares the ROM against text.c on the .f18 files,
word for word, image record included. Host tests load the .f18 files.
- mkmap image writes v4_map_image (the map's description of the system);
hosted and the kernel link the ROM and the map instead of v4_image_64.c.
- Hera (Captain Bob's ruling, option 1): births stream the system the ROM
assembled, as assembled code; hera.4th is unchanged.
test_host_unit's host builds the system with the ROM.
- New parity lines: PARITY:V4_F18 per blob, PARITY:V4_ROM words=2049
rom_hash=0xa6c792b5821010b7 instructions=60725216.
Acceptance: amd64, aarch64, riscv64 boot to ok> with PARITY:V4_POST
tests=538 pass=538 fail=0, POST: PASSED, and PARITY:V4_SYSTEM
word_count=317 dict_hash=0xe5a57f3eb72ff94a on all three -- the same hash
as the prebuilt nucleus gave. make -C v4 test test-32 hosted-check: all
pass. Logs: logs/20261015-141648, -141938, -142350.
Startup cost under QEMU TCG, the ROM's assembly (last V4_F18 line to
V4_ROM): amd64 26.7 s of 132 s to ok>; aarch64 41.0 s of 195 s; riscv64
20.5 s of 101 s. Hosted native: +0.85 s.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
capsules/v4/sys: the nucleus's fourteen sources as .f18 blobs (made once by
v4/tools/v4tof18.py: header becomes the ROM's directives, every other token
and line kept), the memory map as map-<width>.f18 (mkmap sys, from one
table in host_map.h that host_load reads too), and end.f18, the switch-on
state mkimage.c set in C, the image record, and start (IDLE).
test_host_rom_nucleus.c: the ROM, fed those blobs through the console,
builds RAM identical word for word to mkimage.c's, outside its own state
and the image record, and starts at (IDLE), at both widths. The cost:
60.7 million instructions, 1.5 s on the C model.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
word.f18: (NEXT-TOKEN) reads the console or a macro being expanded; labels,
branches by name (a label of the word, or a word, now or later), the
table of branches waiting, end of word, FOR/NEXT/UNEXT, macros, &NAME,
CONST+N, and (ONE), which dispatches a token. A forward branch to a name
not yet defined waits on a placeholder symbol, filled in by (DEFINE).
dir.f18: constant set here #, ", store, and start, which resolves, clears
the tables the ROM used and runs the word it names. main.f18: the ROM
itself, entry 12; on an error it stops at (ROM-FAILED) with the code and
line in its state, for the loader to report. sym.f18 generalized: any
counted string, placeholders, a hash of only a name's own cells.
test_host_rom_asm.c gives test_text.c's own texts (accepted and refused),
its four real definitions and the ROM's directives to text.c and to the
ROM: the same words, the same errors on the same lines, at both widths.
The ROM is 2036 words.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(OP) (LIT) (BRANCH) (BRANCH>) (RESOLVE) (LABEL), entries 6-11: the code
generator of any language built on the machine (MACHINE.md 2.1). asm.c's
layout rules with text.c's branch placement; 400 random sequences agree
word for word with asm.c at both widths, memory-full and out-of-reach are
refused on both sides, and a ref with no branch is refused, not looped on.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(FIND) ( -- rec | 0 ), (DEFINE) ( value kind -- ) and (OPCODE) ( -- op | -1 ),
entries 3-5. Names are refused as text.c's define refuses them, compared
case by case with text.c; a set variable may be set again. 256 hash heads,
records growing up and names growing down in dictionary space, used only
while the ROM assembles. text.c gains the data directives #, and ", (the
ROM's language; its mnemonic and reserved-word tables are data); the
nucleus image is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(NUMBER) ( -- n flag ), entry 2: decimal with a leading -, or hex after $;
1 a number, 0 not one, -1 one that does not fit a cell. Every case is also
given to text.c, the oracle, at both widths.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The ROM's source is F18 code in capsules/v4/rom, assembled off the node by
v4/tools/mkrom.c with the memory map's constants from v4/tools/mkmap.c
(MACHINE.md 2). main.f18 is the entry table payloads call through (2.1);
in.f18 reads the console and splits it into tokens exactly as text.c does,
the token held packed as a counted string. The ROM's state is a reserved
area of the map (host_map.h ROM_*). text.c gains the constant directive,
so the off-node assembler and the ROM speak one language. The map also
names (NUMBER)'s state, for the next task.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A read-only range of V4_ROM_WORDS words at V4_ROM_BASE, above RAM
(MACHINE.md 2, DATASHEET-F18.md 5). Instruction fetch, @p and data fetches
read it; a running programme's store there is an address fault. Attached by
the owner like every device; a reset detaches it. The host node's range is
4096 words. text.c places branches by the whole address space. The
nucleus image is byte-identical.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Captain Bob, 2026-10-14: "change to PC, not SER5. it's not supposed to be
specific, but PC generic." boards/ser5 -> boards/pc, TARGET=PC,
lithos-pc.img; the image was never specific to one machine (the generic
UEFI/ACPI path, FABRIC-3 III). The Beelink SER5 stays named where the text
means the unit on the bench (its Secure Boot, BIOS, serial header) and in
the records. Built: lithos-pc.img, system=v4, with its SBOM.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Captain Bob, 2026-10-14: "v4 is the default build", "no, qemu to v4
too ... we are effectively trying to retire v3", "the 32 bit build needs
to go into the acceptance test too", "i don't care about v3".
- STARFORTH_V4 defaults on (Kconfig default y) and V4_DEFAULT=1
everywhere: every kernel carries v4 and v3 and boots v4; system=v3 boots
v3; STARFORTH_V4=0 builds v3 alone. Board images write system=v4.
- Acceptance (CLAUDE.md): the three ISAs as built (v4, POST passed,
V4_SYSTEM dict_hash equal) and make -C v4 test-32. v3 is not part of
it.
Acceptance run: amd64, aarch64, riscv64 -- PARITY:V4_POST 538/538,
dict_hash=0xe5a57f3eb72ff94a on all three, no unknown word; test-32: 30
suites, 0 failures. The SER5 image under OVMF boots v4 as built, v3 after
the one-word edit. README, BRINGUP, FABRIC-3 XLIII, the register updated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
make boot_image now ends by writing build/boards/<board>/<image>.spdx(.json):
the image and every file copied off it, each with its SHA-256 -- the SBOM
of what is on the medium. It also refreshes sbom.spdx(.json), the source
SBOM: the tracked files the product is built from (kernel, v3, v4,
capsules, boards, tooling, proofs), staged to build/sbom/src so nothing
untracked in the working directory is swept in. Versioned as the kernel
version and git describe. mk/syft.yaml turns on file cataloging with
SHA-256 (a C tree has almost no packages for syft to find: the old SBOM
held one file with a zero checksum). No syft: the build stops.
syft 1.52.0 installed in ~/.local/bin from its GitHub release, checked
against the release's published SHA-256.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Captain Bob's rulings, 2026-10-14: "delete" -- the duplicates listed in
DELETE-CANDIDATES.md are removed (the list stays as the record; the
formal tier's provenance comments repointed at the maintained copies) --
and "rewrite" the README: v2.1.0, the Tripod as Hera/Artemis/Hestia,
kernel-Hermes, identities on thumbdrives, the corrected ACL overhead,
1052-test POST and the current parity line, keyboards and storage, board
images carrying both systems and how to switch, and docs/README.md as
the way in. The book is left as it is, to be reworked later.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/CLAUDE.md Phase 1 output, after the reorganisation.
- docs/README.md: rewritten as the map -- where each kind of document
lives, which one to believe (CLAUDE.md, FABRIC, v4.0.0, reference/,
BRINGUP, code), how to retire a document.
- docs/INDEX.md: regenerated from the tracked tree, 489 rows, every
document with a status (the 2026-06-16 snapshot was stale).
- docs/DEAD-AND-REVISED.md (new): what was abandoned (L4Re, the hal_*
contract, ACL RWT, the Hermes VM, elevation, the knowledge factor, ...)
and what was corrected (ACL overhead, versions, block rules, parity
baseline, ...), each with its date and record.
- docs/DELETE-CANDIDATES.md: 12 duplicates, awaiting a ruling; nothing
deleted.
- CLAUDE.md: versions corrected (3.2.0 / 2.1.0).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
docs/CLAUDE.md Phase 2, 2026-10-14. All moves by git mv; every archived
file carries a header saying what it is, why, and where to look instead.
- docs/reference/ (new): roadmap and versioning policy, M7.1, word-ACL
design, kernel args, hosted acceptance test, dictionary, primitives,
ontology, L8 table -- previously spread over docs/, docs/lithosananke/
and docs/03-architecture/.
- docs/working/archive/historical/: finished work (session plan, test-gap
audit, birthing plan, fix write-ups, the January architecture draft).
- docs/working/archive/superseded/: the Hermes-VM Tripod docs and July
Hermes designs (-> FABRIC-3.5/3.6), the StarForth roadmap, every working
doc already flagged superseded or obsolete.
- docs/working/archive/dead/: the hal_* target contract (never adopted),
L4Re, metrics-and-knobs.
- Papers and proposals into docs/working/papers/.
- References rewritten across the repo (not logs/, not docs/patent/);
CLAUDE.md's documentation section rewritten.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
boards/BRINGUP.md A6, FABRIC-3 SXLIII (ruled: one image, switch at boot).
A STARFORTH_V4 kernel carries v3 too; system= in the boot arguments
chooses, else the build's default (V4_DEFAULT: 1 for plain STARFORTH_V4
builds as before, 0 for board images). make boot_image builds both and
always writes starforth.cfg / cmdline.txt with system=v3 first, and cleans
its ISA's objects first -- that clean was removing build/<arch>/SER5, not
the kernel's objects, until fixed here.
Each ISA: v3 by default (hash unchanged), v4 with system=v4 (538/538).
The SER5 image under OVMF: v3 as built, v4 after a one-word edit on it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
boards/BRINGUP.md A1, FABRIC-3 SXLII. xhci.c keeps a table of controllers
(up to 8) and services each; kernel_main brings up index 0, 1, ... until
there is none. repl.c's and blkio_usb.c's per-slot state is per
controller now (a slot ID is a controller's own), and each USB keyboard
keeps its own last report and lights; an unplugged keyboard releases what
it held.
Test first: keyboard and identity on a second controller behind a bridge,
Zuse on the first -- before, only Zuse; after, all three, on all three
ISAs. Hot-plug and unplug on the second controller. Acceptance unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
boards/BRINGUP.md A3 and S3, FABRIC-3 SXLI. A HID boot keyboard is
configured (interrupt IN endpoint, SET_PROTOCOL boot) and its reports
become the same Linux-keycode events as PS/2 and virtio (usb_kbd.c): the
full layout -- F-keys, navigation block, arrows, Num Lock, keypad. The
console treats Caps and Num Lock as toggles shown on the keyboard's
lights, and the keypad as digits or navigation. While a USB keyboard
runs, its event ring is serviced every tick, not once a second.
Found on the way: with no i8042 (port 0x64 reads 0xFF) the kernel hung at
boot in an endless drain -- a legacy-free PC would stop there. Detected
and skipped now; drains bounded.
QEMU, amd64 with the USB keyboard the only one: 1 2 + . on main keys and
keypad, Caps Lock, Num Lock all right; aarch64/riscv64 configure it too.
Acceptance on three ISAs unchanged; identities hot-plug.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
boards/BRINGUP.md A1, FABRIC-3 SXL. It was matched by QEMU's own IDs on
bus 0 alone, so no real board's was found. Now pci_find_class() reads
every bus/slot/function; pci_bar_size() sizes BAR0; the USB Legacy Support
capability hands the controller over from the BIOS, SMIs off; CSZ=1
contexts are placed 64 bytes apart instead of refused. XHCI_QEMU_ARGS
lets QEMU place the controller as real hardware does.
Test first: an NEC controller behind a PCIe root port -- no attach before,
Zuse minted after, on all three ISAs. Standard acceptance unchanged; five
hot-plugged identities attach. Not rehearsable: firmware handoff, CSZ=1.
Still open: only the first controller found is used.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
boards/BRINGUP.md R2/R3, FABRIC-3 SXXXIX. The entry stub cleans the cache
over the image, builds an identity map (the image's GiB Normal, the rest
Device) and enables MMU and caches at EL2 (or EL1); rpi5_mmu.c makes the
devicetree's RAM Normal and the framebuffer uncached. Mailbox waits give
up after one second; the buffer is cleaned around the exchange; the
framebuffer address goes through dma-ranges, not a Pi 4 mask.
New rpi5-rehearsal target runs the native image on QEMU virt at EL2. It
found three more that would each have stopped the board: the PL011 shift
(mine, c9519173; the host test asserted it), the GIC address not carried
through /soc's ranges (new fdt_reg_address(), checked against the real
Pi 5 DTB), and no timer IRQ at EL2 (HCR_EL2.IMO/FMO/AMO).
Rehearsal: POST 1052 passed, same parity hash as UEFI. Standard acceptance
on three ISAs: POST 1052, hash 0x82f4ae7cb2e2d424.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FABRIC-3 SXXXII.4's deferred LOG-FLUSH, built to the 2026-10-14 rulings
(SXXXVIII): everything a VM logs, at or above the log level, goes into its
own buffer; it goes to Artemis on LOG-FLUSH, when full, and at once on an
ERROR, as one kernel-Hermes LOG_BATCH message she writes on her own turn,
the source being the sender the kernel stamped. Records that cannot go yet
are counted and reported with the next flush. LOG-SHOW (Hera only) reads
the ring back -- it had no reader.
Found by the first test that filled it: ring growth overwrote the oldest
record and put blank slots inside the live range. Fixed test first.
Three ISAs: POST 1052 passed; new parity baseline word_count=526
hash=0x82f4ae7cb2e2d424 (deliberate). CLAUDE.md: Phase 8 PKI closed.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>