A Modus image compiled the Modus sources into a second Modus image that boots and does it again, with no host Lisp anywhere in the loop. The last thing standing in the way was a counter that had never been set. Elsewhere: 256 spec sections in weft, and a VNC drag that went from 18 frames a second to 76.
| Repo | Commits | Lines | Focus | ||
|---|---|---|---|---|---|
| weft | 256 | +16,815 | −2,217 | a spec section per commit | |
| glass | 48 | +4,424 | −824 | the RFB performance week | |
| webrtc-data | 35 | +21,519 | −531 | renamed; a desktop in a browser tab | |
| loom | 16 | +2,751 | −932 | a real browser UI, and reftest runners | |
| modus | 16 | +703 | −208 | the cycle closes | |
| operandi | 12 | +7,516 | −563 | an editor protocol, resumable sessions | |
| cl-nostr | 6 | +1,001 | −57 | gift wrap, the double ratchet, nsite | |
| seal | 5 | +417 | −91 | fast AES-GCM, P-256, DES | |
| shuttle | 5 | +224 | −66 | when a slash is a regex | |
| skep | 4 | +2,537 | −170 | born; a Nostr agent workspace | |
| gesso | 3 | +211 | −31 | dashes, clips, fill rules | |
| pigment | 3 | +809 | −13 | born; the codecs leave weft | |
| stencil | 3 | +108 | −20 | clip paths and dash arrays | |
| cram | 2 | +43 | −7 | a stored-block fast path | |
The smallest section in this issue is the one that matters most, and it needs a careful
statement of what is new. № 2 had a running image build
its own successor from source embedded in its binary.
№ 4 proved with five hash comparisons that the compiler
leaves no trace of the machine that ran it.
№ 19 produced a modus binary carrying the
whole build toolchain. But in every one of those, the first generation came out of SBCL. This
week the loop closes with no host Lisp in it anywhere.
The last obstacle was not architectural.
(gensym) returned #:GNIL every time, because
*gensym-counter* was NIL — its defvar initialiser does not run
at boot. Every other build image sets it explicitly; the self-host source did not. Colliding
GNIL labels broke the native translator.
That limitation is not new — № 13 had already made initialiser thunks fire at boot for one image family, without generalising it. A limitation that has been worked around correctly in four places and forgotten in the fifth is the most expensive kind, because every individual instance looks like an oversight rather than a pattern.
The rest of the week around it is clearing the path: the in-image self-compile’s collector corruption root-caused, a quadratic hash insertion in label emission removed — it was the “GC wall” a self-compile appeared to hit, which is to say it was misattributed to the collector, the way № 16 warned it would be — a 96 MB code-buffer over-allocation stopped, and the compile driver taught to report the failing form and its condition type instead of dying quietly.
Almost every one of weft’s 256 subject lines this week ends in a citation: grid subgrid on both axes, cascade layer ordering, container-query length units, mask images, path-based clipping, relative colour syntax, logical properties under vertical writing modes, collapsing-border table geometry, multi-layer background painting, viewport units in their small, large and dynamic forms, individual transform properties, drop-shadow filters, and lazy image loading measured from a live scroll offset.
Volume like that comes from the harness № 17 established, now grown a third stage. A per-unit oracle maps a feature to its real Web Platform Tests files and prints how many pass. A wave script drives a fleet of workers, each editing exactly one file in an isolated copy of weft and looping against the oracle. And a merge step re-verifies every result in the canonical tree and keeps only what actually helped — which is the part that makes the whole thing trustworthy rather than merely fast.
The isolation is proven rather than assumed: the source registry is pinned to a private tree with inherited configuration switched off, so a worker cannot silently fall through to the real checkout and score itself against code it did not write. Round two added diversified variants of each task and kept the best. That is the same instinct as the census before deletion in № 18 — the mechanism is only worth having if you have checked that it is telling the truth.
Dragging a window ran at 18 frames a second, and the profile exonerated everything obvious: compositing took under half a millisecond, and a drag frame is about 1.6 KB, which encodes in about one. Yet each frame took 56.
That 50 milliseconds is pure TCP latency. The accept path never set
TCP_NODELAY, so Nagle’s algorithm held every small interactive frame for
about 40 ms waiting on the peer’s acknowledgement. A request-and-tiny-response protocol
is Nagle’s worst case, which is why every real VNC server disables it.
The encoding work around it is a catalogue of the same discipline: a window move sending coordinates instead of pixels, per-region damage with region-limited diffing, the frame comparison skipped entirely when a generation counter says nothing changed, a tile encoding chosen to skip a serial compression pass, and then a stored-block variant that is a thirteen-fold encode win from one threshold. Plus a compliance fix worth its own line: those tiles are sixteen pixels, not sixty-four.
Then the drag that adapts, and it is the week’s best small idea. A big drag on a client without a copy-rectangle extension re-encodes the whole window every frame — measured at about 397 KB and 20 ms — while small moves are cheap and look perfect. So rather than pick a rule, the sender tracks how long the oldest unsent change has been waiting, smooths it, and switches: a drag starts opaque and falls back to a wireframe only once the link is demonstrably behind. The decision is driven off the measured backlog rather than off a clock, which is the same correction the ANSI harness made in № 6 when it stopped trusting what it had not observed.
Interoperability took the other half of the week, and each fix names the client that forced it: an older handshake version and its authentication scheme for macOS Screen Sharing; honouring the client’s requested pixel format, including for the cursor, for iOS clients; a different encoding for RealVNC, and then the cursor folded into the frame update, which fixed it properly. The authentication needed DES, which was written from scratch and promptly moved into seal where the ciphers live — the same consolidation reflex as natrium in № 19.
The repository began the week by renaming itself. It implements the WebRTC data-channel
profile and not media, so cl-webrtc overclaimed and webrtc-data does
not — leaving the name webrtc-media well-defined for whatever
fills it later. Naming a thing for what it actually does is cheap now and expensive in six
months.
Then it carried a desktop. The transport became reliable and flow-controlled with round-trip estimation and message fragmentation; the connectivity layer gained server-reflexive candidates, full agent checks and a relay client, proven against a rootless NAT rig rather than asserted; the handshake switched its certificate to an elliptic-curve one, a smaller flight and about 540 ms faster. On top of that, glass over WebRTC: an off-the-shelf browser VNC client accepts a data channel directly as its transport, so the stack is a transparent pipe from a browser tab to a Lisp framebuffer.
The signalling is the part with no server in it. cl-nostr — which had been three weeks old and mostly a client — added encrypted direct messages, gift wrapping, a double ratchet, blob upload and static-site publishing; webrtc-data used them to exchange an offer and answer over direct messages, then to hand out one-time login links. Because a phone has no console, the page grew an on-screen diagnostics panel with a copy button, and the gateway learned to log why a login code was refused — which is the kind of thing you only build after standing in a room failing to log in.
null that
should have been an empty object — and against a third-party client. It also got
resumable sessions and an input line you can type into while the agent is still working.if or while head, and —
the other way — not after a postfix increment.