MESH.md 7b: no message is lost without its sender being told, and no node waits for ever. What v3 does, from the code, and what could not be established. A refusal travels back as a NACK; Hera ends a wait on a stuck node by killing it and tells the others it is gone; a line from the console breaks a wait. Step 6c has the acceptance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
10 KiB
Step 6c: Refusals and Waits — Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: No message is lost without its sender being told, and no node waits for ever.
Architecture: Two message types, NACK and GONE, handled in the nucleus where messages are taken in, passed on and waited for (v4/capsule/core.v4, quit.v4). A node that drops a message records a NACK it owes and sends it when idle. AWAIT ends in an error on a NACK or GONE about the node it waits for, or on text from the console. Hera keeps a table of the nodes she has had born and gets KILL. The lone-node boot hands a waiting node the next console line.
Tech Stack: the v4 nucleus dialect, FORTH (capsules/v4/hera.4th), C99 (v4/system/boot.c, tests), make -C v4, make -f kernel/Makefile.
Spec: docs/v4.0.0/MESH.md section 7b; acceptance in step 6c of section 10.
Global Constraints
- Branch
StarForth-v4.0.0, main checkout. No new branch, no stash, no worktree. Commit and push after every task; end messages withCo-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>. - v4 follows the OS as designed: a statement about what v3 or v4 does today is checked in the code, and where it can be, tried on the binary, before it is relied on.
- No bad code is released: a defect found on the way is fixed, test first, and reported.
- No stubs or stand-ins. Do not change v3.
- Message types: 4 NACK, 5 GONE; each has one word of text, a node's number, and a length of 4.
- Errors: 19 "Message refused", 20 "Node gone", 21 "Interrupted".
- New cells, word addresses relative to
BUF0_Winv4/tests/host_map.h:(REFUSED)at -7,(OWED#)at -8,(OWED)at -112 (8 pairs: to, about),(AWAITING)is the existing(AWAIT-FROM), which is 0 when the node is not waiting. - The queue keeps 36 cells that only types 4 and 5 may use (four of them: seven words, the port, one word of text).
capsules/v4/hera.4th: every block at most 16 lines of at most 64 characters;v4/build/mkcapsule --lint capsules/clean.- Before any QEMU run read
.claude/CLAUDE.md"Running / Acceptance" and the memory noteacceptance-test-rules.md. One QEMU at a time,cleanbeforeqemu, all three ISAs, logs kept. Never delete a log.
Review Focus
- A NACK that meets a full node. Expected: dropped, counted in
(LOST), no NACK owed for it, nothing loops. Test in Task 1. - Two nodes each owing the other a NACK while both have full queues. Expected: neither waits for ever; section 7a's rule still holds. Test in Task 1.
- A GONE that does not come from the centre. Expected: ignored; the way to that node is kept. Test in Task 1.
- Console text for another node passing through a node that is waiting. Expected: it does not interrupt the node it passes through; only text for that node does. Test in Task 2.
KILLof a node Hera never had born, of herself, or twice. Expected: an error with a message, nothing removed. Test in Task 3.
Task 1: NACK and GONE in the nucleus
Files: Modify v4/capsule/core.v4 ((TAKE-KEEP), the queue's room, the errors list), v4/capsule/quit.v4 ((IDLE), (PASS-ON), AWAIT, the messages for 19 to 21, NO-ROUTE, headers for (REFUSED)), v4/tests/host_map.h, v4/tools/mkimage.c, v4/include/v4/message.h (the two types), v4/tests/test_host_mesh.c
Interfaces — Produces: V4_MSG_NACK 4, V4_MSG_GONE 5; nucleus words (OWE) ( to about -- ), (PAY) ( -- ) sends one owed NACK if there is one, NO-ROUTE ( node -- ) forgets the way to a node; variable (REFUSED); AWAIT ( node -- how ) as before when the answer comes, and otherwise ends in error 19, 20 or 21.
- Step 1: Write the failing tests in
test_host_mesh.c(three nodes in a line, 10 with the console, 11, 12):- No way: node 10 sends text to node 77, which nobody has a way to and for which 10's way is "toward 11": 11's
(LOST)rises by one, and 10's(REFUSED)rises by one. - No room: node 12 is made busy (a word that loops a counted number of times without reading) while 10 sends it messages until its queue is full; the next is refused: 12's
(LOST)rises and 10's(REFUSED)rises by one, and when 12 is free every message it did take is done in order. - A waiting sender: 10 does
: W S" 1 DROP" 77 SEND 77 AWAIT ; Wand its line ends "Message refused" with an error; 10 then answers the next line. - GONE: a GONE about 12 arriving at 11 from the port toward 10, 11's centre here, makes 11 forget the way to 12 (
12 (PORT-FOR)gives the default); one arriving from 12's side is ignored (Review Focus 3). A node waiting on 12 ends "Node gone". - A NACK meets a full node (Review Focus 1), and two full nodes owing each other (Review Focus 2): run to quiet;
(LOST)accounts for every message not delivered; every node is back waiting at its ports. - More refusals than room (acceptance 7): nine messages refused at one node in a burst: eight NACKs are owed and sent, the ninth is counted in
(LOST).
- No way: node 10 sends text to node 77, which nobody has a way to and for which 10's way is "toward 11": 11's
- Step 2: Run
make -C v4 run-64-test_host_mesh.c: the new checks fail (nothing sends a NACK). - Step 3: Implement.
(TAKE-KEEP): an ordinary message is kept only if it leaves the 36 reserved cells free; types 4 and 5 may use them. On no room: an ordinary message calls(OWE)with itsfromandto; types 4 and 5 are only counted.(OWE): append the pair at(OWED)if(OWED#)is below 8; otherwise add one to(LOST).(IDLE): before it reads its ports,(PAY): if anything is owed, send the oldest as a message of type 4, length 4, one word, by(PORT-FOR)of itsto(the default way if none; no way at all: count it and go on).(PASS-ON)with no way:(OWE)for an ordinary message, count for types 4 and 5.(IDLE)for this node, type 4: add one to(REFUSED). Type 5: if it came by the port of(ROUTE-DEFAULT),NO-ROUTEfor the node named. Both are then let go.AWAIT: sets(AWAIT-FROM); in its loop, a message for this node of type 4 or 5 whose word of text is the awaited node clears(AWAIT-FROM)and raises 19 or 20 (a GONE only by the centre's port; it also doesNO-ROUTE). On the answer, or any ending,(AWAIT-FROM)is 0.- Errors 19, 20, 21 in
(RAISED)and incore.v4's list.
- Step 4: Run the mesh test at 64 and 32 bits and under the sanitizers; then
make -C v4 -k test. 0 failures;test_host_unit.cstill 538 of 538. - Step 5: Commit
feat(v4.0.0): a refused message is told to its sender -- NACK and GONEand push.
Task 2: A line from the console breaks a wait
Files: v4/capsule/quit.v4 (AWAIT), v4/system/boot.c, v4/Makefile (hosted-check), v4/tests/test_host_mesh.c
- Step 1: Failing tests. In the mesh test: node 12 loops for ever; node 10 does
12 AWAIT; a line typed at the console,65 EMIT, ends 10's wait with "Interrupted" and an error, and then printsA. Text from the console for node 11, passing through 10 while 10 waits, does not end 10's wait and is done by 11 once 10 is free (Review Focus 4). Inhosted-check:printf '5 AWAIT\n1 2 + .\n'printsInterrupted, then3. - Step 2: Run and see them fail (the hosted program ends on
5 AWAIT). - Step 3: Implement. In
AWAIT's loop a message of type 1 for this node whosefromis the node's(CONSOLE)is kept with the messages waiting, as any other is, and then the wait ends in error 21. Inboot.c, a node reading "any port" in the middle of a line, with nothing to give it, returns a new result,V4_BOOT_LINE_WAITING;v4_boot_linecalled again with the next line sends it, which interrupts.v4/tools/hosted.candkernel/src/v4/sk_v4.ctreatWAITINGas "read the next line", printing nothing. - Step 4: Run the mesh test,
make -C v4 -k test,sanitize,hosted-check. - Step 5: Commit
feat(v4.0.0): a line from the console breaks a waitand push.
Task 3: Hera's table and KILL
Files: capsules/v4/hera.4th, v4/tests/test_host_unit.c
- Step 1: Failing tests in the unit of five:
- Killed (acceptance 3): node 11 does
: W 14 AWAIT ; W(it is not wired to 14); Hera does14 KILL; 11's line ends "Node gone"; afterwards14 (PORT-FOR)on 11, 12 and 13 gives each its default way, and on Hera gives 0; aSENDto 14 from 12 comes back refused. - Stuck (acceptance 4): node 13 loops for ever; node 12 waits on it; Hera does
13 KILL; 12's wait ends. - Hera's own wait (acceptance 5): node 12 loops for ever; the console types
12 AWAITto Hera and then12 KILL: the first ends "Interrupted", the second is run, and 12 is gone. - Review Focus 5:
99 KILL,10 KILL, and14 KILLa second time each print a message and end in an error;born_countand the nodes there are unchanged.
- Killed (acceptance 3): node 11 does
- Step 2: Run and see them fail (
KILLis not a word). - Step 3: Implement in
hera.4th, in a new block: a table of 16 pairs filled byBIRTH;(PLACE) ( n -- place | -1 );KILL ( n -- ): not in the table, or this node's own number:." KILL: no such node"and error -1; otherwiseNODE-KILL, the pair removed,NO-ROUTE, and for every other pair a message of type 5 about n bySEND's way. The words for sending a message of a given type with one word of text are the nucleus's (Task 1). - Step 4: Run the unit test,
make -C v4 -k test,sanitize, the capsule lint. - Step 5: Commit
feat(v4.0.0): Hera kills a node by number and tells the others it is goneand push.
Task 4: Bare metal, and the write-up
- Step 1: Three v4 boots with the typed session of the last step and, added to it,
5 AWAIT,1 2 + .. Each log: POST 538 of 538;Interrupted;3; thePARITY:V4_SYSTEMline equal to hosted's. - Step 2:
MESH.mdstep 6c as done, with what is not as intended yet; the note under section 4.1 aboutAWAIT;v4/README.md. - Step 3: Commit with the three logs and push.