<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git/Makefile, branch docs-new</title>
<subtitle>an opinionated terminal multiplexer in C with programmable pane chrome</subtitle>
<id>https://git.sl0p.foo/slosh.git/atom/Makefile?h=docs-new</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/Makefile?h=docs-new'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-09-02T10:32:45Z</updated>
<entry>
<title>docs: deprecate boring design narrative, more trimming and culling</title>
<updated>2026-09-02T10:32:45Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-09-02T10:32:45Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=794e22914666775cab0b1b730a000b2529cec6b5'/>
<id>urn:sha1:794e22914666775cab0b1b730a000b2529cec6b5</id>
<content type='text'>
</content>
</entry>
<entry>
<title>graphics: a frame the client never got must be said again</title>
<updated>2026-08-29T19:31:41Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-29T19:31:41Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=8a8b5e1c7acd269d4a639a40712412a84480ad18'/>
<id>urn:sha1:8a8b5e1c7acd269d4a639a40712412a84480ad18</id>
<content type='text'>
A pane image is re-emitted to the client as decoded RGBA, which for one
screenshot is ~4MB of base64 in a single indivisible message -- and
conn_send silently dropped anything that would overflow the 4MB outbox,
while push_graphics ignored the refusal and the model advanced anyway.
Everything stateful in that dropped frame was lost for good: the image
was marked sent (so its placements drew nothing, forever), and the
deletions rode out with it (so a placement the client had was never
revoked). Reported as screenshots in a recovered pi transcript parked
at a fixed spot on the screen, immune to scrolling and pane swaps --
those were placements whose deletion left with a dropped frame. The
1000x800 one could never be delivered at all: its transmission alone
was bigger than the cap.

So the frame is now transactional. Deletions queue and are repeated
every flush until gfx_commit() confirms the client heard them;
transmissions recorded in a frame are unwound by an undelivered commit
so they are sent again. A flush nobody commits counts as undelivered at
the next gfx_begin(), which is what the `graphics` control command is:
bytes rendered for a CLI, not the client. The server reports the
verdict and keeps the paint pending until it lands.

The outbox cap now bounds the backlog rather than backlog-plus-message,
so one big transmission on a drained queue always fits -- and that
surfaced a second way to lose: a queued image legitimately holds the
backlog over the cap until the client swallows it, and push_frame read
that as a dead client and closed the connection. The session
auto-detached every time a screenshot crossed the wire. A full outbox
is a client that is *behind*, not gone: push_frame now marks the screen
for a full repaint (the dropped diff means the client's cells are
unknown) and retries as POLLOUT drains the queue; a client that truly
left still shows up as a write error there.

The headless suite pins the owed-until-committed contract; the new
tests/live_graphics.py drives the real socket with a
bigger-than-the-cap image and a reader that stalls, and would have
caught both bugs: the flood must not detach, the transmission must
arrive whole, and a placement scrolled away must be deleted -- then
come back on scroll without a second copy of the pixels.
</content>
</entry>
<entry>
<title>release: one command to bump, commit and tag</title>
<updated>2026-08-28T13:44:01Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-28T13:44:01Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=79df05ec1a3c38751956e8f6b122333ed3737efc'/>
<id>urn:sha1:79df05ec1a3c38751956e8f6b122333ed3737efc</id>
<content type='text'>
contrib/release X.Y.Z moves ./VERSION, commits and cuts the annotated tag
together, so the floor and the tag cannot disagree by construction. It
refuses a dirty tree, a detached HEAD, a malformed or non-increasing
version, and an existing tag; notes passed as the second argument land in
the tag message. It prints the push and `make release` commands instead of
running them, because publishing should be deliberate.

contrib/release --check verifies the invariant (VERSION equals the newest
tag) on its own: `make release` runs it before building anything, and the
new pre-push hook runs the same comparison against the commit a v* tag
points at, so a tag made by hand is caught before it escapes the machine.
</content>
</entry>
<entry>
<title>gitignore: DESIGN.md and .fastfeedback are local, not the project</title>
<updated>2026-08-28T13:38:41Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-28T13:38:41Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=a7a20bc026488928b6b65f44924b31615b898263'/>
<id>urn:sha1:a7a20bc026488928b6b65f44924b31615b898263</id>
<content type='text'>
DESIGN.md was a scratch design doc and .fastfeedback a per-repo tool cache;
neither belongs in the tree. Both stay on disk, ignored. Everything that
pointed at DESIGN.md now points at docs/design.md, and the decision counts
quoted from it (which had already drifted: the README said twenty-one, the
docs said twenty-two) are gone with it.
</content>
</entry>
<entry>
<title>windows release: a zip in dist/, built beside the linux tarballs</title>
<updated>2026-08-28T13:38:27Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-28T13:38:27Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=67e0330e5f18e4fe7f87db978071c6afa77ee91b'/>
<id>urn:sha1:67e0330e5f18e4fe7f87db978071c6afa77ee91b</id>
<content type='text'>
make -f Makefile.windows dist stages slosh.exe as
slosh-&lt;version&gt;-windows-&lt;arch&gt;.zip -- a zip, not a tarball, because Windows
is the machine that will open it -- carrying the same licensing paperwork as
the linux tarballs, with docs/windows.md riding along instead of the
manpages. `make release` now builds both architectures via release-windows,
so the windows zips land under the same SHA256SUMS as everything else.
</content>
</entry>
<entry>
<title>path: unit tests for the two spellings</title>
<updated>2026-08-28T13:10:24Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-08-28T13:10:24Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=1e84ca2a1e521ec3ffcb77f1a26e025e55aab90c'/>
<id>urn:sha1:1e84ca2a1e521ec3ffcb77f1a26e025e55aab90c</id>
<content type='text'>
The functions the last two commits fixed are only interesting on the
platform the suite does not run on, which is how they came to be wrong
in the first place. So the Windows cases are asserted under #ifdef and
the POSIX ones assert the opposite: that a drive letter is an ordinary
name here, that a backslash is an ordinary character, that neither rule
leaked across.

That second half is the one worth having. Both fixes are guarded, and a
guard is exactly the kind of thing a later edit removes without meaning
to -- `\` splitting a POSIX path would invent a directory out of a file
called `a\b`, and nothing else in the suite would notice.

Pure functions, so it joins the C unit tests rather than driving a
session. Run on the Windows box as well, cross-compiled: 28 checks
there, 20 here, the difference being the cases each platform has.
</content>
</entry>
<entry>
<title>vendor build: pass ghostty its own version, not our tags</title>
<updated>2026-08-27T14:27:13Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T14:27:13Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=21a2a39a3b27ff4625bbc1d89220fa83e923ecf8'/>
<id>urn:sha1:21a2a39a3b27ff4625bbc1d89220fa83e923ecf8</id>
<content type='text'>
The vendored tree has no .git, so upstream's version detection runs
git describe in *our* repository -- and the moment this repo grew its
first release tag (v0.1.0), ghostty's build refused it: 'tagged
releases must be in vX.Y.Z format matching build.zig'. Our v0.1.0 is
not ghostty's 1.3.2-dev, and upstream is right not to let a stranger's
tag name its build.

-Dversion-string is upstream's own escape hatch for exactly this. The
value is read from the vendored build.zig.zon rather than written here,
so a re-vendor updates it in the one place it already lives. Both
invocations (the host vendor target and the cross-built release legs)
get it through one VT_FLAGS.
</content>
</entry>
<entry>
<title>slosh(5): the file formats get their manpage; slosh(1) learns shaders</title>
<updated>2026-08-27T13:27:51Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T13:27:51Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=b5df6a7b1d73fc9740a077e8eca2d7fc8f6afe60'/>
<id>urn:sha1:b5df6a7b1d73fc9740a077e8eca2d7fc8f6afe60</id>
<content type='text'>
Section 5 is where unix keeps its file formats, and slosh has two
documents sharing one syntax -- the configuration and the layouts -- so
they share a page the way they share a parser. docs/slosh.5 covers the
KDL subset, where the file lives and how reload refuses a broken one,
include and its precedence, every top-level setting grouped the way
config.md groups them, the theme block, the shader vocabulary with the
expression language summarised, the states and their ranking, the keys
block including direct bindings, and the layout schema (tab, pane, and
their properties).

Held to the source like slosh.1 before it: every name in config.c's
KNOWN_TOP -- the list the loader refuses unknown settings against,
itself held to the loader by test_config.py -- must appear in the page,
so a knob cannot be added without this page hearing about it. The pair
must cross-reference each other, and both must be pure ASCII and lint
clean under groff -ww.

slosh(1) grows a SHADERS section -- the one feature a synopsis reader
would never guess a multiplexer had -- and its SEE ALSO now names
slosh(5) and nothing else: the other multiplexers can advertise
themselves.

Install puts the page under share/man/man5, the release tarballs carry
both pages, and the Homebrew formula installs what the commit it is
building has, guarded as before.
</content>
</entry>
<entry>
<title>a manpage: slosh(1), installed, shipped, and held to the source</title>
<updated>2026-08-27T13:17:57Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T13:17:57Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=ee5790225261fb3d473a16a365bedc15619f93cc'/>
<id>urn:sha1:ee5790225261fb3d473a16a365bedc15619f93cc</id>
<content type='text'>
The distributables were a bare binary and a directory of Markdown, which
is fine until somebody types 'man slosh' -- the unix-traditional place
to ask. docs/slosh.1 is hand-written man(7), pure ASCII roff, no
toolchain: the synopsis, the options, the session commands, the default
keys, the mouse (including the new drag-past-an-edge scrolling), where
the configuration lives, what SLOSH_* means in a pane's environment,
files, exit status. The prose docs stay the long form; the page says so
and points at them.

'make install' puts it under PREFIX/share/man/man1 (and uninstall takes
it away), the release tarballs carry it beside the LICENSE -- a tarball
is what a packager starts from -- and the Homebrew formula installs it
when the commit it is building has one, guarded because the formula must
be able to build commits older than the page.

Prose about a program drifts unless something holds them together, so
test_docs.py holds the page to the source the way it already holds the
site to docs/nav: every option main.c's parser compares against and
every word command must appear in the page, every SLOSH_* variable the
code reads or sets must be documented, the roff must be pure ASCII (it
travels through implementations that never heard of preconv), and where
groff exists the page must lint clean at CHECKSTYLE=3 with -ww. Adding
a flag without documenting it is now a failing test, which is the only
kind of documentation policy that survives contact with a deadline.
</content>
</entry>
<entry>
<title>release: one 'make release' for the three target binaries</title>
<updated>2026-08-26T23:28:45Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-26T23:28:45Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=5ba81343523e7b56a1514f2fc98f47973299b8f4'/>
<id>urn:sha1:5ba81343523e7b56a1514f2fc98f47973299b8f4</id>
<content type='text'>
Streamline the release into a single target that stages all three binaries
in dist/ with a SHA256SUMS:

  linux x86_64  }  cross-built here with 'zig cc -target ... -s', static musl
  linux aarch64 }  (runs on any distro), packaged as a .tar.gz with the binary
                   + README + LICENSE + THIRD-PARTY-LICENSES
  macos arm64      handed to 'macos-dist', which signs/notarizes on the builder

zig is already the compiler, so the two linux cross-builds need no extra
toolchain -- just the vendored libghostty-vt rebuilt per -Dtarget. Verified
both: static, stripped (~2MB), and both run (aarch64 under qemu-user).

Factor the version ladder out of the Makefile into contrib/version, and call
it from both Makefile and Makefile.macos, so the linux tarballs, the signed
zip and the binary's own --version are all named from the same tag instead
of Makefile.macos using its own 'git describe --always' scheme (which would
have named the mac artifact after a bare commit hash while linux used the
tag).

  make release-linux            # just the two linux tarballs
  make release REF=&lt;tag&gt;        # all three + SHA256SUMS
</content>
</entry>
</feed>
