The final review of step 6e, by a fresh reviewer told to break it, and
what was ruled: fix them all, test first. Each has a test that failed
before the mend.
- Two texts from one node could be begun in the wrong order when room
for their answers came between the node's look at the first and at the
second. Texts whose answers go by the same port are begun in the order
they came: (D-MASK), quit.v4. 102 of 700 timings were wrong before.
- A word written with PORT! to a node that speaks whole messages, or to
a console's port, blocked its writer for ever and cut off every node
beyond it. It is error 18 now, whether that node is asleep, running
or stuck: fabric.c, node.c.
- A node asleep for room on one wire is woken when another of its wires
is cut.
- A line there was no room for on the lone node's wire was reported as a
fault and ended the hosted program. It is Message refused, and the
prompt comes back: V4_BOOT_LINE_REFUSED. hosted-check has the case;
no bare-metal boot has typed it.
- Ruled: text behind held output for the same port is refused though it
would fit. MESH.md 7d.4 said otherwise and is corrected.
- The removal test also cuts and re-makes a wire, and has an exchange in
which output is held at a full wire: 26,814 disturbances in all.
- MESH.md and v4/README.md no longer say that a stuck node holds up no
other node, that only printing waits, or that no node is left waiting
on one that is gone; 7d.6 lists the limits and what the products do
that they did not before.
make -C v4 test and sanitize at both widths, hosted-check, lint. Three
bare-metal boots: logs/20261013-194827 (amd64), -195131 (aarch64),
-195518 (riscv64). POST 538 of 538, word_count=317,
dict_hash=0xe5a57f3eb72ff94a on all six; the three sessions are the same
character for character. The boots were built with the working tree's
uncommitted change to kernel/Makefile that adds the ISA to the kernel's
version line.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
test_host_mesh.c, 220 checks: three exchanges on the row of three -- text
from the console for node 12 with what it prints and how it ended; node 11
SENDing node 12 text and waiting for its answer; a message node 12 has no
way for and its NACK to node 10 -- are each played once for every engine
step they take with node 11 removed at that step, and again with node 12:
17,642 removals at 64 bits. Every node left comes to rest asleep, blocked
at no port and in no operation, and answers. What is typed is a word
defined beforehand, so that the steps are the exchange's and not the
dictionary being searched.
Removed from the engine, nothing using them: v4_fabric_interrupt_error, a
device's pending hook, v4_node_words_since_look, V4_ERROR_INTERRUPTED.
MESH.md: 4.1's table has the addresses after the ports; 7a and 7b say what
of them still holds; 7b.7's limit is closed; step 6e is written up.
v4/README.md likewise.
make -C v4 test and sanitize at both widths, hosted-check, lint. Three
bare-metal boots with the typed session and a loop printing 400 numbers:
logs/20261013-165124 (amd64), -165428 (aarch64), -165818 (riscv64). POST
538 of 538, word_count=317, dict_hash=0x0951d7e02e251554 on all six, and
the three sessions are the same character for character. The boots were
built with the working tree's uncommitted change to kernel/Makefile that
adds the ISA to the kernel's version line; nothing else differs from this
commit.
The plan's final review is not done yet.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Engine: a wire keeps whether the message at its mark is the one last
looked at; a node's take, move and drop of any other answer 3 and write
nothing (MESH.md 7d.3). A wire cut and made again between a look and a
take no longer has a NACK done as text, or 256 cells written where one
was looked for.
- Engine: v4_fabric_wire_kernel wires a kernel to a port with no queues;
a message for it is refused at once, error 18, on a mesh as on the
products.
- The result of a drop is looked at; a take that does not take has the
message dropped, or gone past: nothing goes round.
- An answer nobody waits for is let go uncounted; (LOST) counts what should
have been delivered or acted on and was not. The flood's account is
exact.
- Error 18 from under a word that was printing empties the data stack, as
a fault does: that word's own values are not left there.
- Tests: the take and drop windows cut at every step, with and without
another message put; a refusal owed to a stuck node whose wire is full.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The result of every take is looked at: a wire cut between the look and
the take leaves nothing acted on (the text before run again, a refusal
counted, a wait ended by an old answer, a way forgotten).
- A node with no room to pass on what another printed, or its word of how
text ended, leaves it on the wire it came by and moves it when there is
room; what is behind it for the same port waits too. Sleep for room (9)
is woken by room on any of the node's wires (engine; MESH.md 7d.3).
- The lone node's host keeps no queue for the kernel's port: a message to
it is error 18. The step limit is counted while the node waits for room.
- A text waiting for room to print is not interrupted by console text;
AWAIT is, as before. (BREAK?) is gone.
- A GONE is believed only by the centre's port; unawaited answers let go
are counted; nothing past a message's last character goes on a wire;
AWAIT sets (AWAIT-FROM) only once the stack has been tried.
- test_host_depth.c: ten paths, each run works or ends in one named stack
error; it fails with (ROOM) or (ROOM-SERVE) taken out.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
Ruled 2026-10-08: A, B, C, D and the guard for a half message. Each with
a scene in test_host_mesh.c that failed first.
A. The words of a message after its first are written from the passes
over the messages waiting by (!W): an offer and a wait for that offer
only (a new engine address). A reader removed in mid-message makes no
error; the rest is let go and counted. Before, the error left the
messages waiting in pieces and the node going round for ever.
B. A store to the wait withdraws every offer; (GATE1) and (PAY-SET) do it
first. Before, text begun after a node went round without sleeping
could go to the port of an older offer: text for one node was done by
another.
C. What a finished text printed is put with the messages waiting, as how
it ended is. With no room either is let go and counted; neither is
begun as a message from (FINISH) any more, where a stuck node on the
way held the whole node.
D. Only what a sender is owed from an earlier text -- its output, how it
ended -- holds that sender's next text back; a GONE does not; and what
cannot be noted holds nothing. A node's own messages keep the order
they were made in.
- A message half taken in when its writer is removed is let go
((MQ-MEND)), not kept as if whole.
- Smaller: a refused line no longer ends a waiting text 'Interrupted';
a node's own queued messages may be passed on by 16 nodes, not 15; no
value is left on the stack when a port goes in mid-refusal.
make -C v4 test and sanitize at both widths, hosted-check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The engine's console release (v4_fabric_interrupt_error), a device's
pending, and the count of words since a look are removed: nothing uses
them. MESH.md 4.1, 7b and 7c say what was built, where it differs from
what was approved and why, and what is still a limit; step 6d is marked
built, with the defects found on the way.
make -C v4 test and sanitize at both widths, hosted-check, lint; three
bare-metal boots. POST 538 of 538, word_count=317,
dict_hash=0x6a39c0bb9d183418 on all six.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found in the five-node test, each with a check that failed first:
- A node with answers it could not give to two stuck senders began no
text at all. It now holds back only text from a sender whose own
answer is still with it.
- KILL waited to tell a stuck node, and a typed line that broke it left
the nodes after it untold. GONE is put with the messages waiting, as
how text ended is, and goes when it can.
- So that 'tell, then wait' still works, AWAIT does what (IDLE) does but
for beginning text: it looks through the messages waiting, offers what
is to be passed on and what is owed, and sleeps in the wait. A waiting
node no longer keeps back what passes through it.
- A node in the wait with an offer on a port that turns out to have
nothing on it is woken and told so (which port: 2 * V4_PORTS + k); no
error is raised on it. (GATE) makes it error 18; (IDLE) and AWAIT let
the message go. Before, removing a node raised 'No one on that port'
on the unrelated text of a neighbour that had something queued for it.
make -C v4 test and sanitize at both widths, hosted-check.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A node stores a word to offer on any of its ports and fetches the wait: it
is blocked until a word comes for it or one of its offers is taken, and
the fabric does the handing over in one step. The port block moves in
the nucleus's map to make room for the new addresses. Nothing uses it
yet. MESH.md 7c; plan step 6d task 1.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The review of step 6c found it did not keep its rule. Ruled: fix them
all; 16 hops.
- Paying a NACK no longer waits on a lower-numbered neighbour; a node
that owes one does not sleep, and goes on taking in what it is sent.
- AWAIT looks first through the messages already waiting.
- A node waiting to write to a removed neighbour gets error 18: the
word that says who is reading also says which ports have anything on
them.
- A console line lets Hera go when she is blocked on the first word of
a message (v4_fabric_interrupt_error, a device's `pending`).
- A refused answer is told to the node that waits, not the one that
answered; one owed to the node itself goes to its own queue.
- A message is passed on by at most 16 nodes, then refused.
- A GONE is believed only when it says it is from the node's centre.
- Tests: the check that tested nothing is replaced; no room for a
waiting sender; the limit that remains is tested as a limit.
- MESH.md 7b's rule reworded to what holds, 7b.7 says where it falls
short; README likewise.
make -C v4 test, sanitize, hosted-check; three bare-metal boots. POST
538 of 538, word_count=317, dict_hash=0xc0769523a47b7dc3 on all six.
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>
MESH.md step 4, the fault found there, as ruled (section 7a): a node
writes only to a neighbour that is reading, keeps what it takes in
meanwhile, and loses and counts what it has no room for.
Engine: two more addresses after a node's ports -- which ports have a
neighbour waiting to write to it, which to read from it. Nucleus: (GATE)
before every message; the messages waiting, a ring of 400 cells, dealt
with when the node is idle. Of two neighbours the lower number may wait
to write (ruled after it was built); NEIGHBOUR tells a node who is on
each port.
test_host_mesh.c, 44 checks: the case that stopped the nodes passes; six
messages from each node to each at once all arrive; 800 at once, the
nodes come to rest and every message arrived or was counted (287 arrived,
714 of all kinds let go). test_fabric.c: the two looks. Both widths,
ASan+UBSan.
POST: twelve cases handed HERE, a cell address on v4, to words that take
a byte address, and so wrote into or read from the nucleus's code at cell
HERE/4. Ruled: left out, marked OPEN, until HERE and the byte words are
made to agree as its own step. POST is 538 cases.
hosted-check on three ISAs, 538/538. Bare metal: logs/20261006-134817
(amd64), -135044 (aarch64), -135440 (riscv64).
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>