fix(v4.0.0): step 6c mended after its review -- what a stuck or gone node can and cannot hold up

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>
This commit is contained in:
rajames
2026-10-07 22:18:06 -04:00
co-authored by Claude Opus 5.5
parent d343001dea
commit 59727f762a
19 changed files with 1058 additions and 66 deletions
+13 -1
View File
@@ -111,6 +111,7 @@ typedef struct {
unsigned last_from; /* the port the last read from "any" took its word from */
unsigned writers; /* bit k: the neighbour on port k is blocked writing to this node */
unsigned readers; /* bit k: the neighbour on port k is blocked reading from this node */
unsigned since_look; /* words taken from this node since it last fetched either of those */
/* DSTACK-DEPTH and RSTACK-DEPTH (D-16). See v4_node_stack_regs_attach. */
v4_cell dstack_reg; /* its word address, or -1 */
@@ -181,7 +182,11 @@ void v4_node_console_attach(v4_node *n, v4_cell addr);
* base + V4_PORTS + 1 which port the last read from "any" came from
* base + V4_PORTS + 2 which ports have a neighbour waiting to write to this
* node: bit k for port k
* base + V4_PORTS + 3 which ports have a neighbour waiting to read from it
* base + V4_PORTS + 3 which ports have a neighbour waiting to read from it,
* bit k for port k; and, V4_PORTS bits higher, which
* ports have anything on them at all: bit V4_PORTS + k
* for port k. A port with nothing on it can never
* be read from or written to.
* The last two are how a node looks before it writes (MESH.md 7a), as the
* F18's io register lets it. They are what whoever connects the ports last
* said, v4_node_port_status; fetching them waits for nothing and changes
@@ -214,6 +219,13 @@ void v4_node_port_attach(v4_node *n, v4_cell base);
/* The neighbour has taken what the node wrote. */
void v4_node_port_served(v4_node *n);
/* How many words the node has had taken since it last looked at its
* neighbours (a fetch of either of the two status addresses). A node that
* looks before it begins a message (MESH.md 7a) and is blocked writing with
* this at 0 is blocked on the first word of the message: nothing of it has
* gone. */
unsigned v4_node_words_since_look(const v4_node *n);
/* THERE IS NO ONE ON THE PORT the node is blocked at -- writing to it, or
* reading from that one port -- and there will not be. A node that can
* take an error (it has an error register and a fault table: node.h,