fix(v4.0.0): EMIT ran past the output buffer; a message's first word was read before there was room for the rest

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>
This commit is contained in:
rajames
2026-10-08 11:46:05 -04:00
co-authored by Claude Opus 5.5
parent 6015c87157
commit 0d91608f39
6 changed files with 129 additions and 2 deletions
+20
View File
@@ -1133,6 +1133,26 @@ int main(void)
}
printf(" %u values may wait on the stack while the next line is typed and interpreted\n", most);
CHECK(most + 4u >= V4_DATA_DEPTH, "the prompt and the interpreter take no more than four cells of the stack");
/* A flush of the output buffer that ends in a fault leaves the buffer full: what is printed next -- the
* error's own message -- must not go past its end (MESH.md 7c.6). */
{
unsigned over = 0, worst = 0, at;
for (k = most > 6 ? most - 6 : 0; k <= most + 3; k++) {
char line[4 * V4_DATA_DEPTH + 16];
size_t n_at = 0;
unsigned m;
boot_bare();
for (m = 0; m < 28; m++) n.mem[OUT_W + 256 + m] = 0;
for (m = 0; m < k; m++) n_at += (size_t)snprintf(line + n_at, sizeof line - n_at, "1 ");
snprintf(line + n_at, sizeof line - n_at, "\n");
(void)say(line);
(void)say("WORDS\n");
for (at = 0; at < 28; at++) if (n.mem[OUT_W + 256 + at] != 0) { over++; worst = k; break; }
if (n.mem[OUT_PTR] > OUT_W + 256) { over++; worst = k; }
CHECK(strcmp(say("1 2 + .\n"), "3 ok\nok> ") == 0, "WORDS with %u values on the stack: the node goes on", k);
}
CHECK(over == 0, "nothing was ever put past the end of the output buffer (it was, with %u values on the stack)", worst);
}
}
/* ---- how much of the data stack a line has ---- */