Skip to content

v0.8.0

Choose a tag to compare

@cwt cwt released this 03 Aug 03:36
· 326 commits to main since this release

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 .redraw request 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 — SIGPIPE is ignored (broken pipe → EPIPE, not
    kill); SIGTTOU/SIGTTIN are ignored (job control in mosh pty); SIGHUP
    is handled non-fatally.
  • Server-side backpressure — DisplayClient.behind with a 768 KiB
    high-watermark; the server throttles feedPty when all clients are behind
    and removes POLL.IN so the loop sleeps instead of busy-spinning.
  • Client-side backpressure — the client stops reading the server socket
    when its own stdout queue exceeds CLIENT_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_SIZE raised from 1 MiB to 16 MiB so large sixel frames are
    delivered instead of dropped-and-looped.
  • Cell-size request flood fixed — sendRequestCellSize rate-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 via client-log-file) so transport
    freeze attribution has both sides of the wire.

Sixel Rendering Stability

  • Delta-based rendering — renderSixelImages now 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.IN removed 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 under docs/development/bugs/001.md–299.md, each with
    YAML frontmatter. A new bugs/index.md provides summary tables and a full
    listing.
  • Broken links fixed — 10 historical BUGS.md links in log.md redirected
    to bugs/index.md; 3 absolute file:/// URIs in TEXT_REFLOW.md replaced
    with project-relative paths.
  • build.zig.zon link in log.md fixed (was resolving to docs/build.zig.zon).

Logging

  • Dual log paths — server-log-file and client-log-file options replace
    the single log-file name. 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-file to the client over the wire
    (new .client_log protocol message, 0x85); the client only enables its log
    when it receives a non-empty path.