Notes for Ronny - 2026-08-04¶
Where the COSMOS file server got to, what is proven, and the one thing I could not close.
The headline¶
The FA protocol layer is done and live-verified. A real SINTRAN III machine (D100) now drives our C# file server through a complete conversation over HDLC: connect, reserve, a two-entry directory walk, and a clean teardown, with no rejections anywhere.
It still does not print a listing. D100 completes the conversation, then reconnects and tries again. The remaining fault is almost certainly in the CONTENT of the 64-byte directory record we synthesise - see section 4.
1. What was wrong, and is now fixed¶
Six real defects, all found by comparing against the D100/D102 captures in
DOC\captures\FA-READ-WRITE-2026-08-04\. Each one only became visible after the previous was
fixed, because each was blocking the conversation earlier.
| # | Defect | How it was proved |
|---|---|---|
| 1 | A request is answered by a ShortAck; the reply is the NEXT exchange | The pattern repeats unbroken through a 100-file listing |
| 2 | XMCSM is the frame's own body length, not the request's | Every Data frame in all four captures |
| 3 | The server holds the directory-walk position; the cursor means "start over / next", not an index | A2 FFFF appears once, A2 0000 102 times, different file each time |
| 4 | 0x078x means "finished" and wants a Close, not a refusal |
The captured teardown |
| 5 | Flags 1 is PER DIRECTION, not one shared number echoed by the answer | 222 matches, 0 mismatches when predicting each side from its own count |
| 6 | An FA body is word aligned - pad an odd length with one zero byte | 480 Data frames, every body length even, none odd |
Defect 5 is the one worth knowing about. The old model - one number per exchange, shared, echoed by the answer - reproduces every capture we hold, but only because the conversation strictly alternates, so two independent counters advance in lockstep and look like one. They come apart only when the two sides drift, which no capture ever does. It cost most of the day.
It also explains XmsgNode.ResyncAcceptDown, the "step the accept down one per XENSE" recovery
someone added earlier: echoing OVERSHOOTS when the peer is ahead, and stepping down walks back to
our real count. That hack was a symptom of this bug. It should be deleted once the new model has
run for a while - I have left it in place for now rather than remove a safety net on the same day.
2. Something D100 told us, which is a nice confirmation¶
I had marked 0x0781 as "observed live, meaning UNKNOWN - possibly ended-early against the
captured 0x0782", and refused to state it as fact.
After the word-alignment fix, D100 started sending 0x0782 instead of 0x0781 on the same
conversation. So the low bit does distinguish an abnormal ending from a normal one, and our
listing is now ending normally. That was the machine confirming the guess, not us.
3. Two harness bugs that cost hours and had nothing to do with the protocol¶
Both made the ND look broken when the fault was on our side. Both are fixed and written up in
tools\README.md.
-
ndterm's login raced. It used fixed delays. D100 was busy just after an XMSG restart, its banner arrived late, the user name went out before theENTERprompt existed, and every step after that was one out of phase. The session never left the login loop and 13 X-C commands each burned a 120-second timeout: 28 minutes, no configuration applied. It now waits for the prompts, and copes with a line that is already logged in. -
The runner blocked on its own stdout. Started hidden with no reader, the console buffer fills and every write blocks about a second - so a connect confirm that should take 200 ms took 23 seconds and D100 gave up. Always start it with
-RedirectStandardOutput.
4. THE ONE THING I COULD NOT CLOSE - and where I would look next¶
D100 asks for exactly two entries (we serve two files), gets both, and then finishes the
conversation without sending ReleaseFileEntry and without ever asking for a third entry.
It then opens a second connection and starts again. A real LIST-FILES uses exactly ONE connect,
so the retry means D100 is not satisfied.
Our reply is now byte-shaped like the real server's everywhere I can check - 98 bytes, same
envelope, same QFORM prefix 8C 4B A2 0000 A2 0000 A2 0001. The differences left are all INSIDE
the 64-byte record, which we synthesise ourselves:
ours real
rec[00] 80 00 "HELLO'" 90 00 "SINTRAN'" <- both values occur, NOT a defect
rec[18] "TXT'" "DATA"
rec[27] 00 07
rec[29] 08 20
rec[32] mostly zero timestamps c1 fa aa 75 ..., sizes
This is the honest gap: nobody has ever watched a real client read a record we built. The test
file says so in its own remarks. The record layout came from RetroFS's NDFS ObjectEntry, which
is right for a file ON DISC; whether the file server sends exactly that, and which fields it
requires, has not been established.
What I would do next, in order:
- Decode
rec[24..31]andrec[32..63]properly against a real NDFS directory. The two-byte values at 26-27 and 28-29 differ between ours and the real one and are cheap to identify. - Serve exactly ONE file and see whether D100 asks for a second. That separates "the record is bad" from "the walk-end signalling is bad", which is the fork I could not resolve today.
- If the record turns out to be the problem, consider replaying a REAL captured 64-byte record verbatim (with the name changed) as a diagnostic - if D100 accepts that, the fault is provably in our record builder and nowhere else.
Step 2 is the cheapest and I would start there.
5. The other thing you should know¶
The Ethernet path has a bug of its own, separate from all of the above. Your HDLC A/B
suggestion is what proved it: with the identical upper stack, HDLC reaches the directory walk while
Ethernet is torn down (kind 0x60) right after the connect confirm and never reaches a single
file-access request. So D19999-over-Ethernet is blocked in NdLinkLayer / EthernetLink or the
emulated card, NOT in XMSG or FA. Nothing in this document's six fixes will help it.
That was a good call - it split one confusing problem into two clear ones.
6. Your "stop-link" hint - what it actually found¶
You suggested cycling the link. There is no STOP-LINK command (X-C answered
** System name STOP-LINK is not known **), but trying it exposed the real fault:
X-C: XMSG Kernel error: XMSG is either not generated, not loaded or not started
*- XMSG error code: -45
XMSG itself was down. I had been staring at the HDLC link while the kernel underneath it was
gone. STOP-X then reported OK: XMSG terminated, so it was half-up rather than absent - exactly
the tell already written up in tools\README.md, which I failed to check.
After STOP-X / START-X, X-C came back healthy and START-LINK,1362,,,-1,, returned Ok. Our
LAPB link went Active in 44 ms.
Two facts about these commands, since they are easy to get wrong:
LIST-LINKtakes no link number - it promptsXROUT system?. MySTOP-LINK,1362line was swallowed answering that prompt, which is why it looked like a rejected command.- After
START-Xthe remote names are gone and must be redefined (DEF-REMOTE,,D103 103etc.) before anything can reach us.
Everything protocol-side is now verified, including the two that were outstanding:
- the restart announce - the first inbound Data frame arrived at Flags 1
0x0000, so D100 did reset its sequence when told. The link also stayed Active through it, which confirms the loop-tick deferral fix. - the per-direction Flags 1 model, re-confirmed on the same clean run.
What D100 actually does now¶
It does not hang. It runs the whole conversation, then retries it three times - connection
numbers 0x0042, 0x0043, 0x0044 - and gives up silently. That is what a record it cannot use
looks like, and it narrows section 4's question rather than answering it.
Two more record fields, measured offline¶
Found without using the machine at all, by comparing every 16-bit field across all 49 real directory records to see which are constant and which vary:
- bytes 22-25 - the entry ORDINAL, counting 0, 1, 2, 3.
ObjectEntry.ToBytesnever writes there, so every record we sent carried zero. - bytes 60-63 (
FilePointer) -0x000078DAon all 49 records, for 49 different files. A block pointer that never changes is not a block pointer, so its meaning is UNKNOWN, but a constant that every record shares is exactly what a reader checks. Ours sent zero.
Both are committed (a85ad3e) and not yet live-verified - see below.
ENNS0 can take XMSG down with it - and it is not needed for the HDLC path¶
Late in the evening a restart failed in a new way: LIST-FILES D103(sys). answered
UNKNOWN REMOTE SYSTEM NAME. The X-C transcript shows why:
DEF-REMOTE,,D103 103 Ok
START-NET-SERVER,ENNS0,,,N *- XMSG error code: -45: XMSG is ... not started
DEF-NETWORK-CONN ... -45
START-LINK,1362,,,-1,, -45 <- so the HDLC link was never started
ENABLE-ROUTE-THROUGH -45
START-NET-SERVER killed XMSG, and every command after it failed the same way. It is
intermittent - one restart in eleven today, so it is not the explanation for everything that went
wrong, but it is worth knowing that a machine which looks thoroughly broken may only have lost
XMSG at that one line.
Two changes, both Ronny's, now in restart-xmsg-cosmos.ps1:
- ENNS0 is opt-in (
-WithEthernet, off by default). It is the ETHERNET network server; a system reached over HDLC does not need it, so the D103 path should not pay for an intermittent crash it has no use for. ABORT ENNS0first, then wait 10 seconds.ABORTis a SINTRAN command at the@prompt, NOT an X-C command - it belongs beside the COSMOS mode file's own@ABORT FSART. If a previousSTART-NET-SERVERtook XMSG down with it, the RT program is left hung and starting it again just repeats the crash. The abort is not complete when the prompt returns, hence the wait.
Skipping ENNS0 gave a completely clean configuration run: DEF-REMOTE,,D103 103 Ok,
START-LINK Ok, ENABLE-ROUTE-THROUGH Ok, no -45 anywhere.
The record diff, finished - only TWO fields are still unexplained¶
With both replies now 98 bytes, a byte-for-byte diff of ours against a real one gives 33 differing
bytes. Naming every one of them against ObjectEntry:
| bytes | ours | real | verdict |
|---|---|---|---|
| body 07 | 81 |
bb |
responder session token - the SERVER picks it, varies across recordings. Not a defect. |
| body 30 | 80 |
90 |
record byte 0. BOTH values occur in the capture on different files. Not a defect. |
| body 32-51 | HELLO/TXT | SINTRAN/DATA | content |
| body 65 | 01 |
00 |
record 35 ObjectIndex - was a defect, fixed: it is the 0-based walk ordinal, not the file number |
| body 69-81 | zeros | dates | see the date gap below |
| body 85 | 01 |
3f |
record 55 PagesInFile - ours is 1 page, the real file is 63. Content. |
| body 87-89 | 19 | 122880 | record 57-59 BytesInFile. Content. |
| body 57, 59 | 0000, 0008 |
0007, 0020 |
record bytes 26-29. STILL UNEXPLAINED. |
So everything is now either fixed, or provably content, or the server's own choice - except record
bytes 26-29, two words that ObjectEntry does not model at all (the same blind spot as bytes
22-25, which did turn out to matter). Real records carry 0x0007 and 0x0020 there. That is the
next thing to chase.
A real gap found on the way: the packed ND date only spans 1950-2013, so a file with a
present-day Windows timestamp encodes as 0 and every file we serve shows no date. It is not
the blocker - the capture has real records carrying 0000 in those fields too - but it is a
visible hole. Left documented rather than papered over with an invented date; it belongs with the
sidecar design.
Where I stopped, and what I need¶
D100's file access is crashed again: the last run produced zero inbound frames, and the
terminal said FILE-ACCESS NOT RUNNING OR CRASHED, even though the restart reported
Server 1 started. No of FACs attached: 30 twice. The fault is inside D100 before it reaches the
network at all.
I have now restarted XMSG on D100 about ten times today. I have not touched the RetroCore emulator process and will not without you saying so - whether it needs a proper restart is your call.
The one thing to do next, once the machine is healthy: run LIST-FILES D103(sys). and see
whether the two new record fields change D100's behaviour. Everything else is in place.
7. Nothing else needs a decision from you¶
Everything above is either done or is ordinary work I can carry on with. The only judgement call I
made without asking was leaving ResyncAcceptDown in place (section 1); say the word and I will
remove it.
Documents: this file, plus
DOC\FA-EXCHANGE-MODEL-AND-DIRECTORY-WALK-2026-08-04.md (the protocol rules) and
DOC\FA-READ-WRITE-WIRE-PROTOCOL-2026-08-04.md (what a request and reply contain).