revert(v4.0.0): step 6d, the wait, is backed out -- two reviews found it unsound

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>
This commit is contained in:
rajames
2026-10-08 07:31:40 -04:00
co-authored by Claude Opus 5.5
parent 10f5ffd00b
commit 6015c87157
20 changed files with 1108 additions and 1203 deletions
+21 -37
View File
@@ -18,6 +18,7 @@ void v4_fabric_init(v4_fabric *f, v4_place *places, unsigned capacity)
f->place = places;
f->capacity = capacity;
f->gone_error = 0;
f->interrupt_error = 0;
for (i = 0; i < capacity; i++) clear_place(&places[i]);
}
@@ -26,6 +27,10 @@ void v4_fabric_gone_error(v4_fabric *f, v4_cell code)
f->gone_error = code;
}
void v4_fabric_interrupt_error(v4_fabric *f, v4_cell code)
{
f->interrupt_error = code;
}
void v4_fabric_grow(v4_fabric *f, v4_place *places, unsigned capacity)
{
@@ -147,15 +152,11 @@ static unsigned hand_over_write(v4_fabric *f, unsigned a)
static unsigned hand_over_read(v4_fabric *f, unsigned a)
{
v4_node *n = &f->place[a].node->n;
unsigned first;
unsigned last;
unsigned first = n->read_port == V4_PORT_ANY ? 0 : n->read_port;
unsigned last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port;
unsigned k;
v4_cell value;
if (n->read_port > V4_PORT_ANY) return 0; /* the wait for an offer only: it reads no port */
first = n->read_port == V4_PORT_ANY ? 0 : n->read_port;
last = n->read_port == V4_PORT_ANY ? V4_PORTS - 1 : n->read_port;
for (k = first; k <= last; k++) {
const v4_wire *w = &f->place[a].wire[k];
if (w->kind == V4_WIRE_DEVICE && w->device->give && w->device->give(w->device->self, &value)) {
@@ -208,35 +209,23 @@ unsigned v4_fabric_step(v4_fabric *f)
for (i = 0; i < f->capacity; i++)
if (awake(f, i) && f->place[i].node->n.reading && !f->place[i].node->n.given) done += hand_over_read(f, i);
/* THE WAIT (docs/v4.0.0/MESH.md 7c). A node in the wait that was given a
* word above is no longer in it: a word for a node comes before its own
* offer. Of the rest, each offer is taken by whoever is ready for it: a
* device that takes, or a node blocked reading that port or any -- which
* a node in the wait is, so of two that offer to each other the one at
* the lower place writes and the other's offers are withdrawn. */
for (i = 0; i < f->capacity; i++) {
v4_node *n;
unsigned k;
if (!awake(f, i)) continue;
n = &f->place[i].node->n;
if (!v4_node_in_wait(n)) continue;
for (k = 0; k < V4_PORTS; k++) {
const v4_wire *w = &f->place[i].wire[k];
if (!(n->offers & (1u << k))) continue;
if (w->kind == V4_WIRE_DEVICE) {
if (w->device->take && w->device->take(w->device->self, n->offer[k])) { (void)v4_node_offer_taken(n, k); done++; break; }
} else if (w->kind == V4_WIRE_NODE && awake(f, w->node)) {
v4_node *r = &f->place[w->node].node->n;
if (r->reading && !r->given && (r->read_port == w->port || r->read_port == V4_PORT_ANY)) {
v4_cell word = n->offer[k];
(void)v4_node_offer_taken(n, k);
v4_node_port_give(r, w->port, word);
done++;
/* a node blocked on the first word of a message to a node that is not
* reading, while a device of its own has something for it */
if (f->interrupt_error != 0)
for (i = 0; i < f->capacity; i++) {
v4_node *n;
unsigned k;
if (!awake(f, i)) continue;
n = &f->place[i].node->n;
if (!n->asking || f->place[i].wire[n->ask_port].kind != V4_WIRE_NODE || v4_node_words_since_look(n) != 0) continue;
for (k = 0; k < V4_PORTS; k++) {
const v4_wire *w = &f->place[i].wire[k];
if (w->kind == V4_WIRE_DEVICE && w->device->pending && w->device->pending(w->device->self)) {
done += (unsigned)v4_node_port_gone(n, f->interrupt_error);
break;
}
}
}
}
/* a node blocked at one port that nothing is wired to */
if (f->gone_error != 0)
@@ -245,13 +234,8 @@ unsigned v4_fabric_step(v4_fabric *f)
if (!awake(f, i)) continue;
n = &f->place[i].node->n;
if ((n->asking && f->place[i].wire[n->ask_port].kind == V4_WIRE_NONE)
|| (n->reading && !n->given && n->read_port < V4_PORTS && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
|| (n->reading && !n->given && n->read_port != V4_PORT_ANY && f->place[i].wire[n->read_port].kind == V4_WIRE_NONE))
done += (unsigned)v4_node_port_gone(n, f->gone_error);
else if (v4_node_in_wait(n)) { /* an offer on a port with nothing on it */
unsigned k;
for (k = 0; k < V4_PORTS; k++)
if ((n->offers & (1u << k)) && f->place[i].wire[k].kind == V4_WIRE_NONE) { done += (unsigned)v4_node_offer_gone(n, k); break; }
}
}
return done;