After the Notes: Brainhat's Own Peer Layer, Motives, and What a Hub Remembers

This is the addendum I promised at the end of the first post. The day after it went up, my father's development notes arrived: 1,114 pages of journal, dated from August 1996 to three weeks ago. The right way to use a record like that is not to read it front to back; it is to ask it questions. I gave it to the same agent that built the service and had it cross-reference every finding I had sent him against the journal, page numbers and all, then read the entries that mattered with the findings beside them. Then I rebuilt the parts of the service the notes said I had done the long way round. This post is what changed, with the pictures I did not have the first time.

What the journal settled

Seven of my twelve findings turned out to be design choices or known corners, written down years before I tripped over them. The temp-file collision that made me serialise transfers is a 2019 decision to seed the file-name generator from the init file so compiles are repeatable; daemon children inherit the seed. The root jail without /tmp is a 2023 choice from the web-server work. The period at the end of every statement is a parser hint: with it, a different grammar rule wins. Reaping is switched off in the shipped build, from July 2025, and a receive deliberately runs the engine's inference pass. The "scared" rule that never fired is vocabulary: the word is both an adjective label and a verb in this word list. And the motive map format I had called undocumented is described twice, in 2017 and again this July.

The biggest thing in the notes was not a finding at all. From December 2023 on, the journal describes a peer layer built into the engine: websocket links between running copies, with protocols for one copy sharing its context with another, for two copies trading contexts, and for querying another copy's memory store. His reply confirmed it: he has not run the text daemon in years; this is how he works. I had built the service on the daemon and the console because that is what the release's help text shows. So round two starts there.

His peer layer, measured

Every knowledge node now also runs the engine's web server, and the shim can open a peer link between two nodes with one console command on the first: wsconnect <ip> 4145 node-a node-b bhpeer. From then on, every statement made on either node reaches the other as it is said. No transfer, no image on disk. Here is one statement crossing, drawn to scale from the measured numbers:

shimnode-anode-b"the X is in the Y"parse, add to context, 37.5 mssaveset with the new statement (bhpeer)merge, ponder, 19.2 msecho"where is the X?""the X is in the Y."+32.4 mstotal 109 ms; bars are to scale, messages keep a minimum gap
A statement said on node-a, and the peer answering the question about it. The bars are proportional to milliseconds; the median from statement returning to peer answering was 8 ms over twenty trials.

Three things the console path could not do, this one does. A rule taught on one node fires on the other. why? keeps its referent on the receiving side, which the whole-context transfer loses. And it costs nothing per transfer, because there are no transfers. The trade-off is honest too: only what is said after the link opens travels, and a link is not forwarded. I tested both of those properly because the difference matters for any design that wants knowledge to spread.

02said it03+23.3 ms04+37 ms05+50.1 ms
Four fresh nodes with a peer link between every pair. A fact said on node-02 arrived on all three others, each over its own link.
06said it07+22 ms08never heard it
Three nodes in a line. The middle node heard the fact after 22 ms; the far node never did, even after another input cycle on the middle. A peer passes on what it is told, not what a peer told it. That is in the journal too, February 2024.
0204060ms after the statement returned on the source"the tie is in the bar" on 02node-03: 23.3 ms after the statement returned03node-04: 37.0 ms after the statement returned04node-05: 50.1 ms after the statement returned05"the mammal is in the house" on 03node-02: 10.4 ms after the statement returned02node-04: 26.7 ms after the statement returned04node-05: 41.0 ms after the statement returned05"the juice is in the restaurant" on 04node-02: 7.2 ms after the statement returned02node-03: 20.5 ms after the statement returned03node-05: 31.5 ms after the statement returned05"the blood is in the store" on 05node-02: 7.9 ms after the statement returned02node-03: 20.9 ms after the statement returned03node-04: 32.2 ms after the statement returned04"the claw is in the antarctica" on 02node-03: 9.0 ms after the statement returned03node-04: 22.9 ms after the statement returned04node-05: 35.8 ms after the statement returned05
Five facts over the mesh, one per row, said on a different node each time. Each dot is when another node first answered the question about it, polled every 5 ms. The later dots are polling order, not the link: the poller asks the nodes one after another.

So a mesh works because every pair has its own link, and a chain stops after one hop. If you want a fact to reach everyone, you link everyone, or you use the transfer for what it is good at: seeding a node with everything another one knows.

Motives, which were blocked, now run

The whitepaper's Phase 2 needed a motive map, and the release ships none. The journal has the format, so I wrote one: three motives, core, kitchen and garden, with deliberately overlapping vocabulary and two trigger sentences. It loaded on the first try. Say "i am hungry" and the engine shifts into the kitchen motive, asks the question that motive carries, and still answers everything it knew before. Two practical things surfaced on the way: the engine compiles each motive file to a binary beside the source, so the directory has to be writable, and a motive's pending question is repeated after every input until you answer it. Both went into the tests.

What a hub remembers

The first post left one number open: a node that gathered everyone else's context seemed to hit a ceiling around the sixteenth merge. I now have the picture instead of the number, and the picture says something different. First, the starting state, with twenty-four nodes each holding five secrets nobody else has:

whose secret was asked aboutnode that was asked010203040506070809101112131415161718192021222324010203040506070809101112131415161718192021222324
Every node asked about every node's first secret. A filled cell is the right answer. Twenty-four of twenty-four on the diagonal, zero off it: nothing leaks between nodes that have not been linked.

Then every node's context is transferred into node-01 in turn, and after each merge the hub is asked every donor's first secret again. Reading down a column is one donor's secret over time; reading across a row is what the hub answered right after that merge:

donor asked about on the hub0203040506070809101112131415161718192021222324merge 1: 02merge 2: 03merge 3: 04merge 4: 05merge 5: 06merge 6: 07merge 7: 08merge 8: 09merge 9: 10merge 10: 11merge 11: 12merge 12: 13merge 13: 14merge 14: 15merge 15: 16merge 16: 17merge 17: 18merge 18: 19merge 19: 20merge 20: 21merge 21: 22merge 22: 23merge 23: 24
The hub's memory, merge by merge. It does not forget gradually. Everything before merge 13 goes at once; the next block survives until merge 21, then goes the same way; at the end the hub answers two of twenty-three donors.

That block-wise flush is not a merge count. I ran the same gather three ways. With one question per merge, the hub still answered every donor after all twenty-three merges. With five questions per donor, it kept fifteen. With every donor re-asked after every merge, as in the grid above, it kept two. What the hub retains depends on how much is asked between merges, because every question is itself an entry in the context, and the context is a bounded buffer that lets go wholesale. The journal describes it as a buffer that is "continually preened," in a 2012 entry. The question for him is now what decides what stays, and whether that depth is something the service can set.

what this means for the design
Many nodes that each own a slice of knowledge, with a router that asks the right one, is the part that holds at every size I tried. One node that accumulates everything is the part that runs into the buffer, and the way to work with that is to decide what a hub is for and ask it only that.

Where the milliseconds go

I also went back and did the benchmarking properly. The shim now times each turn in four phases, and a separate harness talks HTTP to it directly instead of paying for a client process per turn, which is where round one's numbers had about 20 ms of overhead hiding.

020406080ms, measured inside the shimfirst turn, nothing to restoreconnect: 1.0 mssay: 43.5 ms43.5capture: 4.9 ms49.4 mssecond turn, restore and captureconnect: 1.0 msrestore: 6.5 mssay: 49.2 ms49.2capture: 6.4 ms63.1 msthird turn, a pronoun questionconnect: 1.0 msrestore: 4.5 mssay: 43.5 ms43.5capture: 6.5 ms55.5 msconnectrestoresaycapture
Where a turn's milliseconds go, measured inside the shim. Connecting to the daemon is a millisecond; restoring the image into the fresh child is four to seven; the engine's own work is the middle segment; capturing the new image is five to seven.
clientshimdiskengine-b childPOST /turn "who is near her?"read imageconnect: daemon forks a childbreak, receive PORT, stream imagerestore ("Context received"), 4.5 msexit, "who is near her?"parse, ponder, answer, 43.5 ms"luigi is by the princess."break, send SHIM:PORTnew imagecapture, 6.5 mswrite imageengine, reply, 55.5 mstotal 57 ms; bars are to scale, messages keep a minimum gap
One turn, drawn to scale: the client, the shim, the session image on disk, and the fresh engine child that does the work and is thrown away.
050100150turns / sconcurrent sessions204060109.8 /s
Turns per second as concurrent conversations grow, four turns each, two engines. Every session kept its context at every level. The peak is 126 turns per second at sixteen sessions; past that, turns queue behind the one-transfer-at-a-time rule per engine.

Over three hundred single-threaded turns the shim-side mean is 68 ms with a p95 of 88, and a sixty-turn conversation grows its image by about 240 bytes a turn without the restore and capture getting slower. For a symbolic engine parsing and inferring on every turn, on one core, with its whole context loaded and saved either side, I still find nothing to complain about.

One new thing for the list

A node whose peer partner has been restarted keeps working, and keeps listing the link as open, until its next console receive, and then its child dies. It took a specific sequence of my own test suites to expose it, and it is reproduced cleanly in a spike with the log lines. The service works around it by tearing peer links down before it transfers, and by reopening dead children once. It goes back to him as finding thirteen, with the retention question, a request for a motive map of his own, and the newer vocabulary he says we are behind on.

Who actually wrote this, again

Same as last time, and it still matters for how far to trust the numbers. The engine and the notes are my father's. The shim changes, the two new test suites, the dissemination harness, the benchmark harness, the chart pages these figures come from, and most of the words in the documentation were produced with an AI coding agent, Claude Code, working under my direction: I chose what to build from the notes, what counted as passing, and what to send back. Every figure on this page was rendered from a run I watched and can rerun. The journal was worked the same way: the agent indexed the 1,114 pages against my findings and produced the cross-reference; I read the entries it surfaced, checked the quotes against the PDF, and decided what they meant for the design. Thirty years of notes answered in a day is the point of the tool.

takeaway
Reading the author's notes did not shrink the list of findings by much; it changed what they were. Most of them became decisions I could see the reasons for, one became a picture instead of a number, and the mechanism I should have used was there all along, waiting for someone to run the copy with the right flag. The service is better for it, and the second round of questions to him is the kind you can only ask once thirty years of somebody's notes have been put to work, and that is what the tools are for.

Later the same day: the newer vocabulary arrived. What a generated dictionary has to know about the grammar, with the scoreboard.