fix(v4.0.0): step 6e final review -- texts begun in order; a word no one will read lets its writer go; a cut wire wakes a wait for room; a full console wire is a refusal, not a fault

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>
This commit is contained in:
rajames
2026-10-13 20:02:33 -04:00
co-authored by Claude Opus 5.5
parent 06da5700bc
commit 66b26f823c
22 changed files with 1206 additions and 103 deletions
+175 -46
View File
@@ -336,9 +336,10 @@ longer notes a NACK it owes and pays it later, because it keeps nothing:
the NACK is put on the wire at once or counted in `(LOST)` (7d.4); no
room is kept in a node for NACK and GONE, there being no queue in a node;
what ends a wait is looked for on the wires, not among messages a node
has taken in; and the blocked write of 7b.6 cannot happen. The limit of
7b.7, that a stuck node holds up its neighbours, is closed: 7d.6 says
what the limits are now.)*
has taken in; and no message is written a word at a time, so the blocked
write of 7b.6 does not happen to a message (a word written with `PORT!`
is another matter: 7d.6). The limit of 7b.7, that a stuck node holds up
its neighbours, is closed but for what 7d.6 says.)*
**The rule of this section (reworded 2026-10-07, after the review of step
6c): a node is never left waiting, or going round, on a node that is
@@ -474,8 +475,9 @@ Two types, beside text (1), output (2) and done (3):
*(Removed at step 6e, 2026-10-13: no node writes a message a word at a
time, so none is blocked writing one. `v4_fabric_interrupt_error`, a
device's `pending` and `v4_node_words_since_look` are gone from the
engine. Hera can be typed to whatever any node is doing:
`v4/tests/test_host_unit.c`.)*
engine. Hera can be typed to while another node is stuck
(`v4/tests/test_host_unit.c`), unless she is herself in a text that
cannot end: 7d.6.)*
- **A node waiting to write to a neighbour that has been removed** is told:
the word that says which neighbours are reading also says which ports
have anything on them at all, and with nothing there the wait ends in
@@ -484,10 +486,12 @@ Two types, beside text (1), output (2) and done (3):
### 7b.7 Limits
*(**Closed at step 6e, 2026-10-13**, but for the last of these, which
stands. A node that is stuck holds up no other node, and neither does
one that is asleep: what is for it waits on its wire. A node that waits
for an answer passes on what is for other nodes while it waits. What a
stuck node costs now is in section 7d.6.)*
stands, and for what section 7d.6 lists. A node that is stuck no longer
stops its neighbours passing on, taking text or being typed to, and
neither does one that is asleep: what is for it waits on its wire. A
node that waits for an answer passes on what is for other nodes while it
waits. A text whose printing must go by a stuck node still waits for
that node to be killed.)*
- The room kept for NACK and GONE is finite, and so is the list of NACKs
a node owes. Past them one is dropped and counted in `(LOST)`. A sender
@@ -793,7 +797,11 @@ neighbour, which is what a grid of nodes needs (ruling 1).
wire has a queue each way. A node holds nothing in transit.
3. **What a text prints waits for room; everything else is refused at
once.** `SEND`, an answer, a NACK and a GONE either go or are refused.
Printing waits, so that output is not lost.
Printing waits, so that output is not lost. *(As built, after fix
round 1 of tasks 2 and 3: at the node that did the text its answer has
room kept for it and neither waits nor is refused; at a node passing
it on, what a node printed and how its text ended both wait for room,
and nothing else does. 7d.4.)*
4. **A wire holds 1,024 cells each way**, a build parameter, as the number
of ports and the sizes of the stacks are.
5. **The wires' memory is the fabric's own, for now, behind one
@@ -846,7 +854,7 @@ the engine and happens whole.
| 6, first | The mark is put at the message at the front of the wire. |
| 7, next | The mark is put at the message after the one it is at. |
| 8, sleep | The node is blocked until a message has come to one of its wires since it last fetched "which wires have a message". |
| 9, sleep for room | If the wire has A cells free it answers 0 at once. If not, the node is blocked until a message has come as in 8, or a message has come off any of the wires that lead from it since it was last woken that way: it is then answered 0 if the wire has the A cells and 1 if not, and asks which wire has what (10). *(Changed 2026-10-08, fix round 1 of tasks 2 and 3: it was woken by room on the named wire only. A node may be waiting to put on several wires -- output it is passing on, the answers of texts it has not begun -- and can name one.)* |
| 9, sleep for room | If the wire has A cells free it answers 0 at once. If not, the node is blocked until a message has come as in 8, or a message has come off any of the wires that lead from it since it was last woken that way: it is then answered 0 if the wire has the A cells and 1 if not, and asks which wire has what (10). It is woken the same way when any of its wires is cut *(added 2026-10-13, after the final review: it may be keeping something to put there)*. *(Changed 2026-10-08, fix round 1 of tasks 2 and 3: it was woken by room on the named wire only. A node may be waiting to put on several wires -- output it is passing on, the answers of texts it has not begun -- and can name one.)* |
| 10, room | Has the wire A cells free? It answers at once and changes nothing. |
- Each wire has a mark for the node that reads it: the message that
@@ -869,7 +877,9 @@ the engine and happens whole.
while it waits for an answer -- and must still sleep.
- The ports themselves stay as they are, a word at a time, for the two
things that are not messages: a newborn node being sent its nucleus
(section 5), and a node's requests to the kernel (section 8).
(section 5), and a node's requests to the kernel (section 8). A word
written to a node that speaks whole messages, or to a device that
takes none, is refused: 7d.6.
- The looking words of section 7a, and their lower-number rule, are no
longer used by the nucleus.
@@ -901,14 +911,25 @@ waiting, the words that turned it, the list of NACKs owed, and
*(Ruled 2026-10-08, fix round 1 of tasks 2 and 3:)* **but what a
node printed (type 2) and its word of how text ended (type 3) are
not dropped for want of room.** The message is left on the wire it
came by and gone past with next; what is behind it is served, but
for what goes the same way, which is left too so that the order is
kept; and it is moved when room appears. The node then sleeps for
came by and gone past with next; what is behind it is served. What
is behind it and goes the same way is treated as if that wire were
full, though it be short and would fit: output and how text ended
are left too, so that their order is kept, and text, a NACK or a
GONE is refused *(ruled 2026-10-13, after the final review found the
code and this section apart: only printing waits)*. The held message
is moved when room appears. The node then sleeps for
room (9), which room on any of its wires ends. The code before step
6e waited here; letting output go while the line said ok was a
step back.
- *For itself:* it is taken and dealt with as now: text is done; a
NACK is counted; a GONE from its centre is believed.
**Texts whose answers go by the same port are begun in the order
they came.** When one is left on its wire because there is no room
for its answer, every text behind it on that pass whose answer goes
the same way is left too, though room has come since. *(Fixed
2026-10-13, found by the final review: only the room was asked, each
time, and room that came between the look at one text and at the
next began the second first.)*
- *`SEND`, how text ended, a NACK, a GONE* are built in the node's own
memory and put. Refused, a `SEND` is error 19, "Message refused"; the
others are counted in `(LOST)`.
@@ -923,9 +944,11 @@ waiting, the words that turned it, the list of NACKs owed, and
`AWAIT` only.)*
- *`AWAIT`* sleeps (8) and, woken, goes through each wire with first and
next: what is for other nodes is passed on; what would end its wait is
taken -- the answer, a NACK or a believed GONE about the node waited
for, or text from its console; other messages for itself stay on the
wire, in their order.
taken -- the answer, or a NACK or a believed GONE about the node waited
for. Text from its console ends the wait too, and is not taken: it
stays on its wire and is done when the text that waited has been
finished with. Other messages for itself stay on the wire, in their
order.
- *Room for the answer is kept.* Only a node puts messages on the wires
that lead from it, so room it has found there stays until it uses it.
A node does not begin text unless the wire back toward the sender has
@@ -947,6 +970,16 @@ asked by words written to its port, as before, and takes no messages. A
message for that port is refused at once -- error 18, "No one on that
port".
**A line there is no room for** *(fixed 2026-10-13, found by the final
review)*. The lone node's console wire fills if the node takes nothing
from it: it waits for an answer and does not know the lines typed for
its console's, because `(CONSOLE)` was set to another node. The line is
then refused where it is typed, "Message refused", and the prompt comes
back (`V4_BOOT_LINE_REFUSED`, `v4/include/v4/boot.h`). Before, the
hosted program said the node had stopped on a fault and ended, and the
bare-metal one would have stopped for good; no fault had happened. The
node goes on waiting: on a lone node nothing but a restart ends that.
### 7d.6 What will be limits
- A burst to a node that is busy, beyond what its wire holds, is refused
@@ -961,6 +994,47 @@ port".
takes it. A node that is stuck never does: its wires fill, and from
then on whoever sends to it is told at once.
*Added 2026-10-13, after the final review:*
- While a text waits for room to print, its node takes no text, its
console's included: it cannot be typed to until the room comes.
- Text, a NACK or a GONE that arrives at a node behind output that node
is holding for the same port is refused, though the wire has room for
it (7d.4).
- Removing a node tells no one. A node waiting for its answer waits on
until Hera's `KILL` says it is gone, or its console types a line. So
does one whose answer was on a wire that was cut.
- **A word written to a port, `PORT!`.** A node that speaks whole
messages does not read words. A word written to one -- to any node
that has asked for a whole-message operation since it was born,
whether it is asleep at its wires, running, or stuck in a loop -- or
to a console's port, is error 18, "No one on that port", and the
writer goes on (`v4/src/fabric.c`, `v4/src/node.c`; fixed 2026-10-13:
before, the writer was blocked for ever and every node beyond it cut
off, and had it been Hera she could not have been typed to).
Words are for a newborn node being sent its nucleus, which has asked
nothing of a wire. **Still a limit:** if such a node stops reading
before its nucleus is all sent, whoever is sending it is blocked until
it is removed.
- A text of one node's left on its wire for want of room for its answer
holds back every later text whose answer goes by the same port, so
that they are begun in order (7d.4).
*What the products do that they did not before step 6e, beside the
above* (found by the final review, comparing the hosted program with the
one before on typed sessions):
- `5 1 PORT!`, a word to the console's port, is "No one on that port";
it was taken, and the line said ok.
- `SEND-ON` to a port with no one on it leaves nothing on the stack; it
left its four arguments.
- `SEND-ON` with a length above 1,024 or below 0 is error 12; it was
accepted.
- Printing and `GONE` work from one call deeper than they did.
- Two lines that hung now come back to the prompt: a 400-number loop with the
way to the console set to port 0, and a `SEND` with the default way
set to port 0.
### 7d.7 Acceptance
1. *Whole or not at all.* At no step of the engine is part of a message
@@ -1664,8 +1738,7 @@ Each is tested, committed and pushed before the next.
and `-06*` are of the wait as built and are kept as its record.
**6e. Messages on the wire (section 7d).** Ruled 2026-10-08: the
second attempt at 6d. **Built and verified 2026-10-13; its final
review is still to come** (last item below).
second attempt at 6d. **Built, reviewed and mended 2026-10-13.**
- *The wire* (`v4/include/v4/wire.h`, `v4/src/wire.c`, `d5cd6460`,
`83694b18`): a queue of whole messages, one way; put, look, take,
move and drop, each all or nothing. `v4/tests/test_wire.c`, 112
@@ -1673,7 +1746,7 @@ Each is tested, committed and pushed before the next.
- *The engine* (`v4/src/node.c`, `v4/src/fabric.c`): the six addresses
of 7d.3 and the ten operations; the wires' queues given by the
fabric's owner; a device that puts and takes whole messages; the
kernel's port, which has no queue. `v4/tests/test_fabric.c`, 306
kernel's port, which has no queue. `v4/tests/test_fabric.c`, 334
checks.
- *The nucleus* (`v4/capsule/core.v4`, `quit.v4`, `b1d09af0`): `(DO)`,
`(WAY)`, `(ROOM?)`, `(SERVE)`, `(REFUSE)`; `(IDLE)`, `SEND`, `AWAIT`,
@@ -1683,25 +1756,35 @@ Each is tested, committed and pushed before the next.
run by, and the tests' consoles) speak whole messages.
- *Two reviews of the nucleus, each mended* (`6a7d0aa2`, `f1419e73`):
7d.3 and 7d.4 carry what they changed, marked "fix round".
- *A node removed at every step* (acceptance 6):
`v4/tests/test_host_mesh.c`, 220 checks. Three exchanges on the row
- *A node removed, or a wire cut, at every step* (acceptance 6):
`v4/tests/test_host_mesh.c`, 241 checks. Three exchanges on the row
of three -- text from the console for node 12, what it prints and how
it ended; node 11 `SEND`ing 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 whole, and then once for every engine step they take (3,579,
4,214 and 1,025 at 64 bits) with node 11 removed at that step, and
again with node 12: 17,642 removals. After each, every node left came
to rest asleep, blocked at no port and in no operation, and answered
played whole, and then once for every engine step they take (about
3,600, 4,200 and 1,000 at 64 bits) with node 11 removed at that
step, again with node 12 removed, and again with the wire between
them cut and made again. A fourth, in which what node 12 prints is
held at node 11, asleep for room, because the wire to a busy node 10
is full, is disturbed in the same three ways at every step at which
anything on a wire, or what a node asks of one, has changed. 26,814
disturbances in all. After each, every node left came to rest --
asleep for a message, and blocked at no port -- and answered
`1 2 + .`; with node 11 gone, node 12 is wired to node 10 first, as
whoever removed it would, so that it can be asked. None failed. The
test was shown to fail, in 4,376 of them, with the engine changed so
that a wait for room on a cut wire never ends.
whoever removed it would, so that it can be asked. None failed.
*What "answered" allows:* a node that was waiting for an answer from
a node now removed is still waiting when the mesh comes to rest
(7d.6), and the line it is then typed ends that wait, "Interrupted",
before it answers.
The test was shown to fail with the engine changed so that a wait
for room on a cut wire never ends: 2,821 removals of node 11 in the
first exchange, 1,555 in the second, and one in the fourth.
*What is typed in that test is a word defined beforehand, not a line
of several words:* a line is mostly the dictionary being searched,
tens of thousands of steps in which no message moves, and each would
be a removal played to its end. The first exchange as the line
`7 8 * .` takes 38,234 steps; removal was tried at its first 300 and
none failed, and it is not in the suite.
be a removal played to its end. The final review ran the three as
lines of several words, disturbed at every change on a wire, and
found nothing; that is not in the suite.
- *Removed from the engine, nothing using them:*
`v4_fabric_interrupt_error`, a device's `pending`,
`v4_node_words_since_look`, and `V4_ERROR_INTERRUPTED` (the nucleus
@@ -1711,23 +1794,69 @@ Each is tested, committed and pushed before the next.
- *Other tests.* `v4/tests/test_host_unit.c`, 81 checks (Hera and
four); `v4/tests/test_host_depth.c`, 397 checks (acceptance 7 on the
mesh); `v4/tools/depthsweep.py`, 230 runs on the hosted product.
- *Verified.* `make -C v4 test` and `sanitize` at both widths,
`hosted-check`, and `mkcapsule --lint capsules/`: no failures. Three
bare-metal boots, `logs/20261013-165124` (amd64), `-165428`
(aarch64), `-165818` (riscv64), each typing the session of step 6d's
last boots and a loop that prints 400 numbers: 29 values on a line
("Stack overflow"); `WORDS` with 26 on the stack; `AWAIT` with 27,
and `5 AWAIT` ended by the next line ("Interrupted"); all 400
numbers, in order. On all six systems: POST 538 of 538,
`word_count=317`, `dict_hash=0x0951d7e02e251554`; and what the three
boots answered at the prompt is the same, character for character.
- *First verified* at `06da5700`: `make -C v4 test` and `sanitize` at
both widths, `hosted-check`, lint; three bare-metal boots,
`logs/20261013-165124` (amd64), `-165428` (aarch64), `-165818`
(riscv64); `dict_hash=0x0951d7e02e251554` on all six.
- *Seen in the older logs, and not a fault of the node.* In the boots
of 2026-10-08 `WORDS` shows a character twice here and there on
amd64 and riscv64 (`LOG-INFFO-STR`, `QQ.-`). The script that typed
those sessions read QEMU's serial socket only between lines; with
the socket read as it comes, as these three were, there is none.
- **Not done:** the final review of the whole step by a fresh
reviewer (the plan's last step).
the socket read as it comes, as every boot of 2026-10-13 was, there
is none.
- **The final review (2026-10-13)**, by a fresh reviewer told to break
it, and Captain Bob's ruling: "fix them all, test first". Each mend
below has a test that failed before it:
- *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 the same way are now
begun in the order they came (7d.4; `(D-MASK)`, `quit.v4`). The
test lets the room come at each of 700 steps in turn; 102 of them
gave the wrong order 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 (7d.6; `fabric.c`, `node.c`).
- *A node asleep for room on one wire was not woken when another of
its wires was cut.* It is (7d.3).
- *On the two products a line there was no room for was reported as
a fault*, and the hosted program ended. It is "Message refused"
and the prompt comes back (7d.5); `hosted-check` has the case.
The bare-metal host has the same mend (`kernel/src/v4/sk_v4.c`)
but **no boot has typed that case.**
- *Text behind held output for the same port is refused though it
would fit.* Ruled: that is what is meant, and the document was
wrong (7d.4, 7d.6).
- *The removal test proved less than it claimed.* It now cuts and
re-makes a wire as well, and has an exchange in which output is
held at a full wire (above).
- *Statements here and in `v4/README.md` that said too much* are
reworded: that a stuck node holds up no other node, that only
printing waits, that no node is left waiting on one that is gone,
and that `AWAIT` takes its console's text (7b, 7b.6, 7b.7, 7d.1,
7d.4, 7d.6).
- *What the products do that they did not before*, and 7d.6 did not
say, is listed there.
- *Not mended:* `ROUTE_MAX` in `v4/tests/host_map.h` is used by
nothing, as it was before this step.
- *What the review did not cover:* the aarch64 and riscv64 products
and bare metal; its own sweeps at 32 bits; the unit of five with
wires cut and re-made.
- *Verified after the mends.* `make -C v4 test` and `sanitize` at both
widths, `hosted-check` (230 depth-sweep runs, and the new case),
and `mkcapsule --lint capsules/`: no failures. `test_fabric.c` 334
checks, `test_host_mesh.c` 241, `test_host_unit.c` 81,
`test_host_depth.c` 397, `test_wire.c` 112. Three bare-metal boots,
`logs/20261013-194827` (amd64), `-195131` (aarch64), `-195518`
(riscv64), with the session of the boots before: 29 values on a line
("Stack overflow"); `WORDS` with 26 on the stack; `AWAIT` with 27,
and `5 AWAIT` ended by the next line ("Interrupted"); a loop that
prints 400 numbers, all of them, in order. On all six systems: POST
538 of 538, `word_count=317`, `dict_hash=0xe5a57f3eb72ff94a` (the
nucleus changed, so the hash did); and what the three boots answered
at the prompt is the same, character for character, as each other
and as the boots before the mends.
- **Not done:** a second review, of the mends.
7. **A second unit; scaling while running; sleep, wake and kill by
command.**