Repository navigation
v0.8.0
szn v0.8.0
22 commits since v0.7.0. 802 tests passing.
Headline: Client Hardening, Sixel Stability, and a 51-Bug Sweep
v0.8.0 is a reliability release. The interactive client has been hardened for
unstable connections: mosh transport drops, SSH disconnects, and slow links no
longer freeze or crash, and the server correctly backpressures panes to the
link speed. Sixel rendering has been reworked from a per-frame full-redraw to
a delta-based change-tracking pass — images that haven't moved or changed are
no longer re-emitted. 51 bugs (#249–#299) were fixed across every subsystem,
and the documentation bundle was brought into OKF v0.1 compliance.
Client & Backpressure (#298)
The v0.7.0 server-side non-blocking socket fix solved the server freeze, but
the client still blocked on blocking stdout and a blocking server socket.
Under real network latency (mosh, tailscale) the client could stall, the server
could busy-spin, and pipes could fill silently.
- Non-blocking client I/O — stdout and the server socket are
O_NONBLOCK;
writes use bounded buffers that drop frames on overflow rather than blocking.
A.redrawrequest requests a full repaint once the pipe drains. - Client drain on dead stdin — the interactive client no longer exits on
stdin EOF or transport drops (mosh/SSH); it stops polling the dead fd and
probes it periodically for revival. - Signal hardening —
SIGPIPEis ignored (broken pipe →EPIPE, not
kill);SIGTTOU/SIGTTINare ignored (job control in mosh pty);SIGHUP
is handled non-fatally. - Server-side backpressure —
DisplayClient.behindwith a 768 KiB
high-watermark; the server throttlesfeedPtywhen all clients are behind
and removesPOLL.INso the loop sleeps instead of busy-spinning. - Client-side backpressure — the client stops reading the server socket
when its own stdout queue exceedsCLIENT_OUT_HIGH_WATERMARK, tripping
server-side backpressure. Frames are delivered at link speed rather than
dropped. - Buffer caps raised —
MAX_OUT_BUF,MAX_CLIENT_OUT_BUF, and
MAX_PACKET_SIZEraised from 1 MiB to 16 MiB so large sixel frames are
delivered instead of dropped-and-looped. - Cell-size request flood fixed —
sendRequestCellSizerate-limited to
2/sec (was firing 200k+ times on a DCS false-positive sixel trigger from
opencode's capability queries). - CSI 14 t reply leak fixed — the cell-size query response (
ESC [ 4 ; h ; w t) is now parsed and consumed on the client side rather than being
forwarded to the pane as a keystroke (and echoed by the shell). - Client diagnostics — the client writes its own log to
~/.szn/szn-client.log(configured viaclient-log-file) so transport
freeze attribution has both sides of the wire.
Sixel Rendering Stability
- Delta-based rendering —
renderSixelImagesnow computes per-pane image
anchors and IDs and only re-emits a sixel when its anchor OR content changed.
A pure replacement at the same position draws just that image (no erase-all);
a full erase-all+redraw happens only when an image is removed or moved.
Frames stay small instead of growing with each accumulated image. - Ghost overlay fixed — exiting the alternate screen (e.g. quitting yazi)
frees the alt screen's sixel images; images whose marker cells were
overwritten (refcount 0) are no longer drawn. - Oversized sixel dropped gracefully — images larger than 8 MiB are dropped
at capture time with a log message rather than producing frames that overflow
the client buffer and freeze the display. - First-sixel cell-size race fixed — when a sixel arrives before the
terminal's cell size is measured, the image is buffered (pending_sixel) and
replayed once the measurement arrives, avoiding a cursor misplacement after
the first image. - Sixel-wait 100% CPU spin fixed — panes with a pending sixel have
POLL.INremoved so the event loop sleeps rather than busy-spinning on level-
triggered PTY data.
51 Bug Fixes (#249–#299)
Since v0.7.0 (bugs up to #248), every remaining entry in the bug tracker has
been resolved. Key areas:
| Area | Bugs |
|---|---|
| Grid / history ring-buffer integrity | #249, #264, #265 |
| Sixel refcount bookkeeping | #253, #278, #279, #291, #292 |
| Layout & pane swap correctness | #250, #284 |
| Copy-mode search & cursor bounds | #251, #276, #281 |
| Cursor/clamp calculations | #277 |
| Memory lifecycle (leaks, UAF, double-free) | #230, #266, #267, #269, #280, #282, #283, #285, #286, #287, #288, #295, #296 |
| Configuration parser edge cases | #259, #275 |
| DoS / safety caps | #228, #260, #261, #270, #297 |
| Performance (O(n²) → O(1) / O(n)) | #225, #240, #241, #242, #252, #276 |
| Dead code & code quality | #243, #244, #255, #256, #257, #273 |
| Protocol & architecture compliance | #258, #293 |
The bug tracker is now fully resolved: 286 fixed, 9 false positives, 1
intentional, 3 open (false positives with non-standard status formatting in
the original file).
Documentation
- OKF v0.1 compliance —
BUGS.md(4107 lines, 236 KB) split into 299
individual documents underdocs/development/bugs/001.md–299.md, each with
YAML frontmatter. A newbugs/index.mdprovides summary tables and a full
listing. - Broken links fixed — 10 historical
BUGS.mdlinks inlog.mdredirected
tobugs/index.md; 3 absolutefile:///URIs inTEXT_REFLOW.mdreplaced
with project-relative paths. build.zig.zonlink inlog.mdfixed (was resolving todocs/build.zig.zon).
Logging
- Dual log paths —
server-log-fileandclient-log-fileoptions replace
the singlelog-filename. Both are disabled by default for privacy.
"default"resolves to$XDG_STATE_HOME/szn/szn.log(server) and
~/.szn/szn-client.log(client). - The server sends the configured
client-log-fileto the client over the wire
(new.client_logprotocol message, 0x85); the client only enables its log
when it receives a non-empty path.