Handoff, 2026-08-11¶
Six tasks closed against real hardware, one feature delivered and proved unattended, and a new plan opened. What is NOT done is at the bottom and is the part to read twice.
1. What was proved on the machine¶
Each of these was confirmed by asking D100, not by reading our own log.
| Task | Result |
|---|---|
| #38 pull a file | 20400 bytes off D100, SHA256 identical to the original |
| #23 create by quoted name | 20400 bytes written; D100's own file server reported the file back at that size |
| #33 the sync daemon | a file dropped in a watched folder, nothing touched on D100, and LIST-FILES showed FILE 80 : (PACK-ONE:SYSTEM)WATCH3:TXT;1 |
| #30 APPEND-REMOTE-BATCH | D100 acknowledged the letter and answered with our serial 1B echoed, XRNCO in parameter 1 |
| #31, #18 | closed earlier, both proved live |
2. The three things that unblocked everything¶
Two diagnostics were never connected. XmsgServerHost.Log was wired on the relay path and not
on the Ethernet one; XmsgLayer gave no way to reach XmsgNode.Log at all. With them plugged in,
a push that read as plain silence turned out to be D100 refusing us twice, by name. A diagnostic
that is not plugged in is not a diagnostic.
--resync-hard. ~~D100 DOES reset its expected-from-us when it receives our reachability
request; D103 over HDLC does NOT. Two machines, opposite behaviour, so it stays an explicit flag.
This is what made the push work.~~
REFUTED 2026-08-17 - do not use this flag. D100 does NOT reset, on the announce or on a reachability request. Measured by reading D100's own table:
X-C->LIST-SYSTEMS-> XROUT system100-> system19999prints the sequence pair it holds for us, and across an--announce-restartAND a--request-linkit stayed atsend 64 / receive 65while ignoring every0x0000frame we sent.--resync-hardzeroes OUR counter against a peer that keeps its own, which puts every later frame behind-sequence, where SINTRAN drops it without an error - and that silence is what made a second runner process look "refused" for a night.The counter in
xmsg-sequence.stateis the correct value: carry on from it. Thereceivecolumn ofLIST-SYSTEMSequals our next Flags1 and is the oracle to check it against - confirmed twice, at 65 (0x0041) and again at 124 (0x007C). SeeWHAT-WE-DO-NOT-KNOW.md.
The envelope seed is a per-link constant, verified across the capture corpus, so it is now
remembered in xmsg-link-seed.state. That is what lets the daemon address a machine that has not
spoken since we started - and it is remembered, never invented. A node we have genuinely never
heard from stays unreachable and says why.
3. A conclusion I published and then withdrew¶
I wrote that D100 refuses a datagram sent while the previous is still unaccepted - a pacing limit.
The successful run disproves it: SendData: 4 frame(s) appears ten times and every one is
accepted. The real fault was being AHEAD from the first frame, and the rejections that looked like
a rate limit were one drift reported once per frame. The wrong section is kept and marked in
PULL-PROVED-AND-PUSH-XENSE-BURST-2026-08-11.md rather than deleted.
4. The sync daemon, and four defects found by running it¶
Recipe (CORRECTED 2026-08-18 - --announce-restart REMOVED too; --resync-hard went on 2026-08-17,
see section 2):
--sync <folder> --sync-user <user> --sync-to <node>.
--announce-restart is not a harmless extra - it is what makes the peer refuse the conversation.
On a freshly brought-up D100 a push carrying it was refused, and the peer named the reason:
[push] <- node 100 REFUSED us: XRDDF (3) - XDTYP=0x0017 XDSCR=0xFFFD
XDTYP 0x0017 is an InitializationNak; XRDDF is "Another port already has this name". Drop the
flag and the identical transfer completes - and so does the next one straight after it, with no
bring-up and no link cycle between. Both flags are now gone from the recipe for the same reason:
they were in it because somebody once needed them, and nothing reported what they cost.
Re-proved on that recipe against D100 on 2026-08-17, all three in one sitting and with the counter
simply carried on from xmsg-sequence.state:
- pull
BIGPSH3:TXT, 20400 bytes, SHA256d523bddc...8d5aebda, identical to the pushed original; - push under a quoted new name, confirmed on the machine as
FILE 76 : (PACK-ONE:SYSTEM)SEQFIX:TXT;1; - the daemon carried a watched file, confirmed as
FILE 99 : (PACK-ONE:SYSTEM)SEQOK:TXT;1.
It now runs from cold too, with --originate-from-seed. Without that flag the daemon holds every
transfer with "the link has not learned the peer yet" until D100 addresses us once, so a person has
to touch the machine first. With it, the daemon opens the link from the seed remembered in
xmsg-link-seed.state and D100 accepts:
[seq] node 100: link opened from the REMEMBERED seed 0x5B, starting Flags1 0x011E
from the store (nothing was received first)
Nothing inbound before that line; FILE 102 : (PACK-ONE:SYSTEM)PROOF:TXT;1 appeared on the machine
with nobody at the console. The starting Flags1 comes from the sequence store exactly as it does
when an inbound frame opens the link, so this obeys the sequence law rather than working around it.
Left OFF by default: proved on D100 only, and D103 over HDLC differs on the related reset question, so it stays explicit until a second machine agrees.
Every one of these was found by running it against D100, none by reading it:
- a file was re-queued on every scan (the ledger only records success, so nothing stopped it);
- an addressed filespec
D100(SYSTEM)."WATCH1:TXT"killed the whole runner - the wire carries neither machine nor user, and the exception escaped the loop tick; - our own resync dropped the in-memory link, stranding us from a peer we had just been talking to;
- the seed was not remembered.
Since then the ledger survives restarts, and with no listing it answers whether the remote file exists - so a file carried before is REPLACED rather than created again. The one case that is still wrong: a file deleted on the machine behind our back. Only a listing catches that, and the daemon says so in its own startup line.
5. Chat¶
Server, clients, channels and aliases, plus a room a SINTRAN terminal user can join
(chat join|say|who|nick|part). The rules live once in ChatRoom with no transport, and both
doors carry them - ChatServer was rewritten onto it with all 26 of its tests passing unchanged.
Guide: CHAT-USER-GUIDE.md.
6. NOT DONE - read this part twice¶
The live two-user chat run. Nobody has sat at a real SINTRAN terminal, joined a room and
talked to a second user. Until that happens the chat is a working implementation, not a proven
one. This is the shortest remaining job: two terminals into our node, chat join on each, watch a
line cross.
A client-side directory listing. It is the one thing that would close the daemon's last blind spot. It was deliberately NOT written: shipping an unproven walk into the planner's decision path risks reintroducing the create-refusal that was just fixed, because a wrong cursor would report "not there" and create. Write it against the machine, not against reasoning.
Why a second terminal command renders nothing under test - now understood, and it is NOT a
server fault. DrainSegmentedOutput returns while OutstandingOutputCount is above zero, and a
reply's final frame is tracked as outstanding like any other. Telling the server directly,
NotifyAck(node, flags1), RELEASES it and the second reply goes out in full. Sending the same
acknowledgement as a frame from the test client does not. So the remaining gap is in the client
acknowledgement or its routing - a small, well-aimed question, and the place to start if those
tests are ever unblocked. OutputWindowDiagnosticTests proves both halves.
A wrong answer I published on the way. I first reported that even NotifyAck failed to
release the window. It had not failed - my diagnostic scanned the frame body for a 07 03 length
BDAT header, found none, and called a reply that had been sent in full "nothing rendered". The
scanner is now deliberately crude and framing-independent. A diagnostic that can only fail one way
is worse than no diagnostic.
7. Next¶
Task #40 is the new one, and it is meant to be done together: move the chat ONTO the ND - a
CHATSV RT program and a CHAT PROG client in PLANC, so two ND machines chat with the C# node
switched off. Plan, with the open questions and the failure modes stated in advance:
PLAN-SINTRAN-NATIVE-CHAT-RT-AND-PROG.md.
The first move there is to read, not guess: how a real RT server waits (XFTRAD, FSART) and
how a real PROG waits for a keystroke and a message at once (TADADM, the spooling programs).
Both are on the machine and can be read.