Queues of whole messages on the wires between nodes (v4/wire.h), the six
addresses and ten operations a node asks of the fabric (MESH.md 7d.3), and the
fabric's pool of queues. Engine only: the nucleus does not use any of it yet.
The ports in the host map move to BUF0_W - 140 so that the new addresses are
not among the nucleus variables, with a compile-time check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two root causes behind what the reviews of the wait found, both in
the code as it stands. Each with a check that failed first.
EMIT stored its character and only then looked whether the buffer was
exactly full. A flush that ended in a fault -- the stack too deep to
begin a message -- left the buffer full, and the fault's own message was
then put past its end, with (OUT^) never again at the end to be flushed.
The 28 cells after the buffer happen to be unused, so nothing showed;
the wait had put the ports there, and the node blocked writing to one.
EMIT now sends a buffer it finds full before it stores.
AWAIT, and (GATE) when a neighbour is writing, read a message's first
word and then used stack that was not known to be there. With 27 or 28
values on the stack, or 28 or 29 calls deep, a line typed during AWAIT
was taken and never answered. (ROOM) tries six cells and four return
entries first: if they are not there the text ends 'Stack overflow' with
nothing read. (GATE) tries the three cells its writing needs before a
first word goes; it was safe before only by the order things were done.
v4/tools/depthsweep.py runs every message path of the hosted product at
every depth of both stacks: each line typed must be answered and the
node must come back. hosted-check runs it.
make -C v4 test and sanitize at both widths, hosted-check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ruled 2026-10-08. The engine, the nucleus and their tests are as they
were at dfabfa46, before step 6d; the code of the wait and its tests are
in history at 075385ab..2cf37aab. The limit of MESH.md 7b.7 stands: a
node that is stuck can hold up its neighbours until Hera kills it.
MESH.md 7c is kept as the record of the design as approved, and 7c.6 says
what was built, what the two reviews found, the cause the findings share
-- a node's message machinery runs on the stacks its text is using, and a
fault abandons whatever was in progress -- and what a second attempt
must settle before anything is built.
Kept from it, in hosted-check: a line that leaves 29 values on the stack,
and WORDS with 26 values on it. Both hung the products at some commit of
the wait.
make -C v4 test and sanitize at both widths, hosted-check, lint; three
bare-metal boots typing both. POST 538 of 538, word_count=317,
dict_hash=0xc0769523a47b7dc3 on all six: the hash of step 6c's code.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The review of step 6d found it not sound. Mended here, with a check
that failed first: a text that left 29 values on the stack hung the
node for ever, on the products too -- the passes over the messages
waiting that follow every text need four cells, and a stack fault in the
middle of one left the messages in pieces. Such a text ends 'Stack
overflow', as before step 6d. hosted-check types it.
The other findings are open and are written under step 6d in MESH.md
section 10; the README no longer says the aim is met.
make -C v4 test and sanitize at both widths, hosted-check. The bare
metal boots have not been made again.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Text for a node from its console, arriving while the node waits for an
answer (AWAIT), is kept as any message is; the wait ends in error 21,
Interrupted; and the text is then done as usual. Console text for
another node, passing through a node that is waiting, does not end its
wait.
On the hosted program 5 AWAIT ended the program with "the node stopped".
The boot now reports that the line is waiting, the host reads the next
line and hands it over, which breaks the wait, and then has the kept line
done. The bare-metal host does the same; it is booted in the commit that
follows.
test_host_mesh.c: 76 checks, both widths and the sanitizers. hosted-check
types 5 AWAIT and two more lines: Interrupted, then 3 and 56.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A node writing to a node that was stuck, and was then killed, stayed
blocked for ever: killing a stuck node could cost its neighbours. And on
the hosted and bare-metal products a write to an empty port, 5 7 PORT!,
ended the program with "the node stopped".
Now a node that can take an error, blocked writing to or reading from one
port that nothing is wired to -- nothing ever was, or what was there has
been killed or the wire cut -- has error 18, No one on that port, raised
on it and goes on. A bare node waits as on the fabric; so does a node
whose neighbour is asleep, and one reading "any port".
test_host_unit.c: Hera kills node 14 while node 12 is blocked writing to
it. It failed first: 12 stayed blocked. test_fabric.c: a bare node still
waits. hosted-check types 5 7 PORT! and goes on; it failed first too.
make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and
riscv64 boot, POST 538 of 538, dict_hash 0x5f0a949a6fc8ef2b on all six:
logs/20261007-121743, -122008, -122337.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Captain Bob was told that v3 leaves the words POST's cases define in the
dictionary, and ruled that v4 should. That was false: v3's run_test_suite
puts the dictionary back after each word's cases (test_common.c:333,
:365). Shown that, he ruled that POST leaves nothing. The boot now
seals the system, runs POST, and has the node do COLD, whose printing is
not shown; PARITY:V4_SYSTEM is the system as sealed.
A case the node does not come back from ends POST there, named, with how
many were not run: it would have stalled the boot for hours, where the
capsule had ended it. The runner's test of it now uses a word that
really never ends.
hosted-check also requires that RS1 and T{ are unknown after boot, and
boots a program whose POST has failing cases (tests/post_cases_fail.c):
PARITY:FAIL, POST: FAILED, no prompt, none of a case's printing shown.
Comments and documents that still named the POST capsule or its two
hooks are brought up to date; NUCLEUS.md 6.3 says what v3 does, with the
lines, and how the wrong ruling came about.
make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and
riscv64 boot, POST 538 of 538, word_count=314, dict_hash
0x220ab283a504a3b3 on all six: logs/20261007-112638, -112901, -113220.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The boot runs POST with the runner (v4/system/post.c) after the capsules:
it feeds the 538 cases to the node and judges them from outside. A
case's printing no longer reaches the console. What the cases define
stays in the dictionary, as in v3; the system is sealed after POST and
the boot prints PARITY:V4_SYSTEM word_count=N dict_hash=..., as v3 prints
its parity after POST.
Gone: capsules/v4/post79.4th and its blocks 7000 up; the harness words;
(CATCH) and (EMIT-HOOK), with what EMIT and the prompt loop did for them.
The generator runs v3 on the lines as the kernel sends them, without the
capsule's "T| ". One expected result follows from that: >IN.initial
prints 6, not 9.
make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and
riscv64 boot, POST 538 of 538, word_count=411, dict_hash
0x6fb1d09418b189ee on all six: logs/20261007-105118, -105335, -105651.
T{ is unknown at the prompt; RS1 prints 42 42 before and after COLD. A
scratch build with one expectation changed names the case and ends
PARITY:FAIL, POST: FAILED.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v4/system/post.c: for each case it empties the node's data stack, sends
DECIMAL FORTH DEFINITIONS, sends the case's lines keeping what the node
prints, reads the stack, and judges: an error exactly if one is expected,
and otherwise the stack and every character printed. A failing case is
named with what it printed and left. Nothing of it is on the node.
Its tests are in test_host_quit.c, on that test's node: 29 checks of cases
that must pass and cases that must fail -- a wrong value, depth, order or
output, an error wanted or unwanted, a case of two lines, depth-only
cases, the starting state, more printing than is kept, and a line that
never comes back. Both widths and the sanitizers.
The boot still runs the capsule; that is the next commit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ruled 2026-10-07. BLOCK, BUFFER, UPDATE, SAVE-BUFFERS and EMPTY-BUFFERS
are each one kernel request on port 0, by block number only. The node
has a window of four slots in its memory, as a v3 VM has; the kernel
copies blocks into it, keeps the record of which block is in which slot,
and decides when a block is written (v4/system/blocks.c). The node keeps
no record and gives the kernel no address, so a request cannot overwrite
the node's code. When a block is written stays FORTH-79's: UPDATE marks
it. When the chain of devices changes the slots are let go, as in v3.
The window is 512 cells more than the two buffers were; the dictionary
space ends that much lower, at 13824.
MESH.md 8.5 had said, from the review, that a v3 VM's BLOCK is the
kernel's buffer. It is not, and the section now says what was reported,
what v3 does, and what was ruled.
make -C v4 test, sanitize and hosted-check pass; amd64, aarch64 and
riscv64 boot, POST 538 of 538, the same hashes on all six:
logs/20261007-092835, -093112, -093456.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A block request with fewer than two values on the stack is refused; it
had acted on whatever the stack ring held and stopped the node.
Bare metal: when the kernel's chain takes the place of POST's block RAM
the node's two buffers are emptied, so it no longer holds POST's copy of
a block; and the chain's fast RAM is cleared, so a node cannot read what
was in the kernel's heap.
blocks.c is built with each test under that test's own warnings and
sanitizers; it had been left out of both. The hosted link cleans its
object directory first: it had linked the withdrawn store_v3.o left there
from the day before.
node.h and DECOMPOSITION.md D-19 no longer describe the message device or
the four registers as current. MESH.md 8.5 records two findings for
ruling: a node's own copy of a block, and a block read over a node's code.
From a clean build: make -C v4 test, sanitize and hosted-check pass;
amd64, aarch64 and riscv64 boot, POST 538 of 538, same hashes, blocks 1
and 2047 clean at the prompt: logs/20261007-085017, -085254, -085636.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A block is a kernel request, as ENGINE.md 3.3 has it: the node puts the
block's number and the address of 256 cells on its stack and writes the
request to port 0, and the kernel leaves the status there. The requests
are -1, read, and -2, write, the same for every node. v4/system/blocks.c
serves them from the kernel's block subsystem, which is v3's. The four
storage registers are gone from the engine.
The device that spoke block messages (4a505a15) is withdrawn with its
test and its message types: Captain Bob ruled on 2026-10-07 that it, a
node's own drive, and nodes with no storage had left the OS as designed
(docs/v4.0.0/MESH.md 8.5).
Hera no longer sends POST to the nodes she births: POST is the kernel's,
once. Every node has its kernel on port 0; it serves a node's blocks and,
for Hera alone, her requests for nodes and capsules.
Bare metal: the node boots and is POSTed against POST's own block RAM,
and the kernel's chain -- fast RAM, the ramdrive, the virtio disk -- is
set up after POST and before the prompt, as on the v3 path. The disk is
read and not written: nothing in v4 yet gives the owner's word that it
may be formatted. A hosted program has the chain's fast RAM, as hosted
v3 has with no disk. Error 17 is Storage refused.
make -C v4 test and sanitize pass at both widths; hosted-check passes on
three ISAs; amd64, aarch64 and riscv64 boot, POST 538 of 538, with the
typed session: logs/20261007-081603, -081839, -082226. The hashes are
the same on all six.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What is on a storage port (v4/system/storage.c): it takes Block read,
Block write and Block data and answers Block data or Block done. It
holds a view of a common chain, a private chain, or both; private block k
is number 2^32 - 1 - k. Behind it is v3's block subsystem, reached
through v4/system/store_v3.c, the one v4 file that includes v3's headers.
v3's block code links here with its two device back ends, its log and
its clock, and nothing else of v3.
v4/tests/test_store.c: 36 checks at 64 bits, 35 at 32, and under the
sanitizers. docs/v4.0.0/MESH.md 8.3, 8.4.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MESH.md step 3. The ports are the transport; the message is what is
transported: to, from, type, heat and TTL, ACL tag, sequence, length, then
text four characters to a word.
- quit.v4: a node with nothing to do is blocked reading "any port"; text
for it is interpreted; (FINISH) sends what it printed and then how the
text ended, and it waits again
- core.v4: EMIT keeps what is printed, (FLUSH-OUT) and (HDR) send it to the
sender on the port the message came on. EMIT still needs one free data
cell and no more; it works on the return stack and in A and B
- message.h/.c: the same format for whatever is on a port and is not a node
- boot.c: the boot is the node's console on port 1 and its kernel on port 0
- the prompt tests are a console that speaks messages
- gone: v4_line_begin, v4_line_done, v4_line_status; writing a node's input
buffer and setting its P from outside; any use of CONSOLE-TX
Verified: make -C v4 test (test_host_quit.c 1283 checks, the full-stack
figures unchanged) and make -C v4 sanitize pass; hosted-check passes on
three ISAs with POST 550 of 550; clean qemu with STARFORTH_V4=1 passes POST
and answers lines typed at each prompt on amd64, aarch64 and riscv64
(logs/20261006-110551, -111621, -111341). -110837 is an aarch64 run ended
by the test wrapper's limit while still in UEFI firmware; it shows nothing
about v4.
Not done: KEY, EXPECT and QUERY still read the console's input registers;
a message not for this node is let go (step 4).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MESH.md step 2. A capsule of F18 code is the words a neighbour writes to a
node's port: for each stretch of memory, "@p a! @p push", the address and
count, "@p !+ unext" and the words; then a jump to the start. A node born
empty executes that from its port, so it needs nothing in it beforehand.
- capsule.h/.c: v4_capsule_write, any node's memory as such a capsule
- mkimage writes the nucleus so, to capsules/v4/nucleus-64.f18, and the
addresses a host needs as a C file; the memory image is no longer linked
into either product
- mkcapsule is unchanged: the nucleus capsule is a built file kept under
capsules/, as BLOCK_MAP.md is, and is baked, hashed and signed with the
rest
- boot: the node is born empty (v4_image_born); the nucleus capsule is
found, its hash and signature checked, and given to the node a word at a
time as it reads its port; PARITY:V4_NUCLEUS carries its name and hash
Verified: test_fabric.c (59 checks, both widths, and under ASan and UBSan):
a memory with a programme and scattered words arrives word for word in an
empty node and runs. The nucleus capsule rebuilds byte for byte.
hosted-check passes on three ISAs; clean qemu with STARFORTH_V4=1 on amd64,
aarch64 and riscv64 takes the nucleus in, passes POST (550 of 550) and
answers lines typed at each prompt (logs/20261006-102421, -102706,
-103048).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MESH.md step 1, in the engine, which knows nothing of StarForth or of any
kernel.
- node: V4_PORTS ports (8), a build parameter; "any port" and the port the
last such read came from; a read blocks until the neighbour writes, as a
write blocks until the neighbour reads; v4_node_born: empty, P at "any
port"
- exec: a fetch from a port -- @ @b @+ @p, or of an instruction word when P
is a port -- waits for a word; a node executes what arrives at a port
without advancing P; a blocked node goes on from the slot it stopped at
- fabric: the nodes there are and the table of how their ports are wired,
both changed while the nodes run; devices on a port; asleep and awake; a
step is every unblocked node executing one instruction word, then every
write with a reader waiting being handed over
- DECOMPOSITION.md section 6: four named ports withdrawn for V4_PORTS
numbered ones and wiring as data, as ruled
Verified: tests/test_fabric.c, 53 checks at both widths: two nodes exchange
words; an empty node is filled through its port by a device, and by another
node, and runs what it was sent; a word is passed on by a node in between;
a waiting node executes nothing; the wiring is changed while they run; a
node is put to sleep, woken and removed while looping; a node is born while
others run; the fabric is given more room. make -C v4 test and make -C v4
sanitize pass. The single-node products are unchanged: hosted-check on
three ISAs, and clean qemu with STARFORTH_V4=1 on amd64, aarch64 and
riscv64 with lines typed at each prompt (logs/20261006-074907, -075150,
-075532).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ENGINE.md 3b, the node's side of ruling A (a word's code is the node's, its
accounts the kernel's).
- dict.v4, system.v4: (WORD-DEFINED) ( xt -- ) is run when an entry is
made, (WORD-FORGOTTEN) ( w -- ) when FORGET or COLD removes entries; with
0 there no one is told, as on the hosted product
- test_host_quit.c: a kernel that keeps the list of words and is checked to
hold exactly the node's dictionary after definitions, a vocabulary, an
abandoned definition, FORGET, a refused FORGET and COLD; KERNEL-WORD
called from the prompt and from a definition
Fixed, found while writing that test: since the capsules moved from build
time to boot time (294e6946), what COLD returns to and FORGET protects was
still the nucleus alone, so COLD lost U*, U/MOD and BYE and FORGET U* was
allowed. The boot now seals the system when it has loaded it
(v4_image_seal), and hosted-check checks COLD, the capsule word after it,
the refused FORGET and BYE.
Verified: make -C v4 test passes at both widths (1283 checks in
test_host_quit.c); hosted-check passes on three ISAs; clean qemu with
STARFORTH_V4=1 on amd64, aarch64 and riscv64 passes POST, and COLD, U*
after it, FORGET U* (refused), an unserved kernel word and BYE typed at
each prompt are answered correctly (logs/20261005-193045, -193307, -193636).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The boot is now nucleus, forth79.4th, POST, prompt, on both products.
- forth79.4th: U* and U/MOD, the capsule's first colon definitions. They
are in the FORTH-79 Required Word Set and neither v3 nor v4 had them.
- post79.4th: 550 cases, 126 of the 130 required words. 443 are v3's with
v3's result. The rest follow three rulings (2026-10-05): address-
dependent cases are checked for count, not value; where v3 departs from
FORTH-79 the standard's result is expected; words v3 has no case for get
cases written by hand. v4/tools/post79_rules.py holds each exception
with its reason and docs/v4.0.0/POST79.md lists them all.
- every case starts from an empty stack, DECIMAL and FORTH DEFINITIONS
- the boot requires POST's tally line with fail=0
Verified: tests=550 pass=550 fail=0 and identical PARITY lines on hosted
amd64, aarch64 and riscv64 (make -C v4 hosted-check) and on bare metal,
clean qemu with STARFORTH_V4=1, on the same three (logs/20261005-1619xx,
-1621xx, -1625xx). A U/MOD broken on purpose fails five cases and stops
the boot. make -C v4 test passes.
Not shown: all words but those two are still assembled, so POST has so far
tested the assembled words. Nothing was typed at a bare-metal prompt.
Open: PAD 42 OVER ! faults on v4 (D-1).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
capsules/v4/post79.4th: 537 of v3's POST cases for the FORTH-79 Required
Word Set, each carrying what the hosted v3 binary did with the same line
(error or not, the stack, the length and checksum of what it printed).
Written by v4/tools/mkpost.py. The harness is FORTH-79 plus the two
nucleus hooks. docs/v4.0.0/NUCLEUS.md section 6.
- NODE-ERROR has a FORTH name: how a definition in FORTH raises an error
- the boot passes POST only on seeing its tally line with fail=0
- POST is not in the boot yet (V4_POST_AT_BOOT=0); make -C v4 post runs it
Result: tests=537 pass=439 fail=98. The 98 are not yet sorted into v4
defects and differences needing a ruling; v4/README.md has a first reading.
Verified: make -C v4 test passes; hosted-check passes on three ISAs; clean
qemu with STARFORTH_V4=1 on amd64, aarch64 and riscv64 reaches ok> with the
same hashes as hosted (logs/20261005-1601xx..1604xx). Nothing was typed at
a bare-metal prompt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One boot, v4/system/boot.c, for both products: it starts the nucleus image,
finds each capsule in the baked capsule directory, recomputes its hash,
checks its signature, gives its blocks to the node a line at a time, and
prints PARITY:V4_NUCLEUS, PARITY:V4_CAPSULE and PARITY:OK before the prompt.
A line the node does not accept ends the boot with the capsule, block and
line named. docs/v4.0.0/NUCLEUS.md.
- hosted Linux product for amd64, aarch64 and riscv64 (make -C v4 hosted);
make -C v4 hosted-check boots all three and requires identical output
- the kernel's v4 entry (STARFORTH_V4=1) calls the same boot
- capsules/v4/forth79.4th, block 6000: no definitions yet
- mkimage builds the nucleus only; no FORTH source is compiled at build time
- capsule_blocks.c: the Block-header parse, free of any VM, for every loader
Verified: make -C v4 test passes; hosted-check passes on the three ISAs with
the same hashes; the kernel compiles with STARFORTH_V4=1 on the three.
Not verified: no bare-metal boot of v4 has been run.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With the stacks counted and guarded (D-16) their size is a parameter of
the node. The host node, which runs the interpreter and the compiler
under the user's programme, gets 32 values and 32 return entries; a mesh
node keeps the F18's 10 and 9. Nothing else about the mechanism or any
word's definition changes.
- stack.h: V4_DATA_RING and V4_RET_RING are build parameters; the
Makefile sets them for the host-node tests.
- tests/test_host_quit.c no longer assumes a size: it fills the stacks to
whatever they are, and takes every exit with the return stack full.
- Measured on the host node now: 28 values on a line, 29 waiting between
lines, words 31 deep from the prompt.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
WORD, the dictionary words and the code generator run underneath
whatever the user has on the stacks, which are ten and nine deep (D-2).
Measured, they used five to seven data cells of their own, leaving a
line about three. Each now keeps what it works on in its file's
scratch cells and has at most three cells on the data stack; , calls
nothing; and the longest chains of calls are shorter.
The public words of core.v4, input.v4 and dict.v4 get dictionary
headers. NUMBER is split so the interpreter can have a flag instead of
NODE-ERROR. The host-node tests share one memory map, host_map.h.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The first layer of the compiler capsule, on the host node: TIB >IN SPAN
SOURCE BL EXPECT QUERY WORD ENCLOSE CONVERT NUMBER and the comment
words. The definitions are text, v4/capsule/core.v4 (the core words
they rest on) and v4/capsule/input.v4, assembled by the text assembler,
which can now read a file.
EXPECT, QUERY, WORD and ENCLOSE behave as v3's. CONVERT and NUMBER are
FORTH-79 (ruled 2026-10-04): any BASE, a double in the standard order,
and NUMBER returns a signed double or sets NODE-ERROR.
Executed at both cell widths against C and against values recorded from
the v3 binary.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Node memory is a build parameter. Tests named test_host_*.c are now
built with V4_NODE_WORDS=16384, the host node that will hold the
compiler, the dictionary and the text being compiled; every other test
keeps the 1024-word mesh node.
The first such test checks the node at that size and the text
assembler's branch placement, which only matters there: a branch slot
that cannot reach the whole node is not used.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without -fno-sanitize-recover=all, UBSan prints a report and the run
carries on to "all v4 tests passed". The sanitize build now aborts on
the first report. Verified by rebuilding the test-side shift bug found
in 981f4180 with these flags: the run exits 1 at the report.
Both test binaries now also depend on v4/Makefile, so a flag change
rebuilds them instead of leaving stale binaries in place.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
First code for StarForth v4 (JUSTIFICATION.md section 10, step 1): one node
of the 32-instruction core as a C99 model, with cell width as a build
parameter.
- Node: P, A, B, F18 circular stacks (10 and 9 deep, D-2), word-addressed
memory (D-1), 5% guard bands on every bounded list.
- Instruction word: six 5-bit slots in 32 bits at every cell width.
- Executor: all 32 opcodes of DECOMPOSITION.md 1.3. Cell arithmetic wraps
explicitly; no signed overflow or implementation-defined shift.
- Heat: per-opcode and per-call-target counters and the anti-clock, driven
by instruction retirement (1.4, D-6 interim).
- Slot packer and runner for tests, and a reference unsigned multiply in
plain C99 with no 128-bit type.
Tests run at 32- and 64-bit cells, and under ASan and UBSan. They cover
every opcode and execute the first section 4 definitions (NIP SWAP OR
NEGATE ROT 0< 0= 2DUP - U<) against the C operation each stands for.
UM* as written in section 4 is exact only while u1 <= 2^(n-2). Two known
failing cases are pinned in test_foundation.c until it is rewritten.
DECOMPOSITION.md: record D-9, the instruction word is 32 bits at every
cell width (ruled 2026-10-02).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>