<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git/contrib, branch v0.1.1</title>
<subtitle>an opinionated terminal multiplexer in C with programmable pane chrome</subtitle>
<id>https://git.sl0p.foo/slosh.git/atom/contrib?h=v0.1.1</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/contrib?h=v0.1.1'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-08-27T13:27:51Z</updated>
<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>macos-remote-build: tolerate notarization ticket propagation lag</title>
<updated>2026-08-27T12:43:02Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-27T12:43:02Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=36dd24325b3a070389d4d45c98dc5822ea9d2c27'/>
<id>urn:sha1:36dd24325b3a070389d4d45c98dc5822ea9d2c27</id>
<content type='text'>
The post-notarization Gatekeeper check ran spctl exactly once, immediately.
A bare CLI binary cannot be stapled (stapler only takes .pkg/.dmg/.app), so
there is nothing on disk to carry the ticket and Gatekeeper must fetch it
online from Apple's CloudKit -- which lags the notary's own 'Accepted' by
seconds to a couple of minutes. The single check raced that propagation and
failed the whole release with 'Unnotarized Developer ID' for a build Apple
had already accepted (status: Accepted), taking the good linux tarballs and
the SHA256SUMS down with it.

Poll spctl instead: pass the instant the ticket appears, and if it still has
not after ~2 min, report it as pending and let the release stand -- the notary
verdict is authoritative and codesign already vouched for the signature. Any
other rejection still fails hard, so a real signing problem is still caught.
The accepted vs pending states are told apart by 'Notarized' vs 'Unnotarized'
so there is no false positive.
</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>
<entry>
<title>version: derive from git tags instead of a hand-bumped constant</title>
<updated>2026-08-26T23:22:34Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-26T23:22:34Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=276ea2610c783bd61789691b910b4be4dbeae890'/>
<id>urn:sha1:276ea2610c783bd61789691b910b4be4dbeae890</id>
<content type='text'>
VERSION was a constant in the Makefile that had to be bumped by hand and
could silently disagree with the git tag a release was cut from. Derive it
instead, with a committed ./VERSION as the floor and no-git fallback:

  on a tag        v0.2.0            -&gt; 0.2.0
  past a tag      v0.2.0-5-gabc123  -&gt; 0.2.0-5-gabc123   (sorts after 0.2.0)
  before any tag  0.1.0-197-g2d1815 -&gt; ./VERSION + commit count + short id
  no .git at all  0.1.0             -&gt; ./VERSION verbatim (cgit snapshot -&gt;
                                       Homebrew source build)
A dirty tree appends -dirty. The commit count keeps 'brew upgrade' ordering
monotonic before the first tag exists.

version.h now bakes just VERSION (dropping the redundant +GITDESC, which the
derived string already carries). contrib/brew-release reads the base from
./VERSION rather than grepping the Makefile's VERSION line, which is now a
$(shell ...) expression it could not parse.
</content>
</entry>
<entry>
<title>macos-remote-build: stop rsyncing 850M of caches to the builder</title>
<updated>2026-08-26T22:21:01Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-26T22:21:01Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=2d1815f8555394a14d331e251a55cd7d7918ab66'/>
<id>urn:sha1:2d1815f8555394a14d331e251a55cd7d7918ab66</id>
<content type='text'>
The working-tree sync excluded two of the three gitignored
vendor/libghostty-vt zig caches but not zig-pkg/ (354M), so every
run silently re-pushed it. With rsync -a and no progress output that
looked like a hang on the rsync step.

Add the missing zig-pkg/ exclude (keeping the list in step with
.gitignore), plus web/ (~430M landing page/demo) and tests/fuzz/
(~120M corpus), neither of which is an input to `make all` -- all
the builder runs. Cuts the sync from ~1.2G/48k files to 350M/9.6k.

Only affects the default working-tree mode; REF=&lt;tag&gt; builds check
out on the builder via git and were unaffected.
</content>
</entry>
<entry>
<title>macos: the pipeline is the project's, the identity is yours</title>
<updated>2026-08-26T22:08:52Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-08-26T22:08:52Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=aa2ec9b65dbee07886556b0d8ae353cb462964b9'/>
<id>urn:sha1:aa2ec9b65dbee07886556b0d8ae353cb462964b9</id>
<content type='text'>
Makefile.macos was kept out of git because it named a team, a person and
a mac. But almost all of it is mechanism -- build, sign, zip, notarize,
staple, unlock a headless keychain -- and that is the project's, not the
releaser's. Keeping the whole file out to protect four values meant the
one machine that had it was the only place a release could be cut from,
and that nobody could read how signing worked without being handed it.

So the split is inverted. Makefile.macos, the builder doc and the scripts
are tracked; macos-release.mk holds TEAM_ID, SIGNER and BUILDER and is
the only thing gitignored. macos-release.mk.example is the template.

The identity strings are now assembled from SIGNER and TEAM_ID rather
than written out, which means an unconfigured checkout composes
"Developer ID Application:  ()" -- plausible, wrong, and diagnosed only
by codesign a minute into a build. require-identity and require-builder
catch it before anything runs and name the file to create; `help` prints
"(unset)" rather than the empty shape.

macos-remote-build now pushes macos-release.mk instead of Makefile.macos.
That is the better arrangement anyway: the builder gets its pipeline from
git at the commit being released, so a release is signed by the flow
belonging to it and not by whatever the laptop was carrying.

MACOS-BUILDER.md was in the same position -- local, and referenced by a
file that is now tracked -- so it becomes docs/macos-release.md with the
certificate name and notary key id replaced by their shapes.
</content>
</entry>
<entry>
<title>brew-release: cut a release from the Linux box it is cut from</title>
<updated>2026-08-26T21:37:41Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-08-26T21:37:41Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=626af41b693aa98cd7d86cabd7c6f1a94d01ec41'/>
<id>urn:sha1:626af41b693aa98cd7d86cabd7c6f1a94d01ec41</id>
<content type='text'>
Nothing about updating the formula needs a mac or needs brew -- it
resolves a ref, fetches a tarball, hashes it, rewrites three stanzas.
But it hashed with `shasum`, which is perl's and only usually present
on Linux, where `sha256sum` is coreutils and always is. Take whichever
exists, as macos-remote-build already did.

The version count no longer falls back to 0 when the commit is not in
this clone. `0.1.0-0-&lt;short&gt;` is older than every version published, so
a release cut from a clone that had not fetched yet would have looked
like a downgrade and never installed; say so and stop instead.

Also written down: why the formula builds from source rather than
shipping the notarized zip the mac builder makes -- brew's curl sets no
quarantine attribute, so notarization gains a formula nothing, and the
build is a minute with zig as its only dependency. If that ever changes
the answer is bottles, which are additive and cost a per-macOS-version
matrix, not a cask.
</content>
</entry>
<entry>
<title>macos: sign and notarize on a mac that is not this one</title>
<updated>2026-08-26T21:30:58Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-08-26T21:30:21Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=4bd545a46891b6ac5bb7599911d370978b84c234'/>
<id>urn:sha1:4bd545a46891b6ac5bb7599911d370978b84c234</id>
<content type='text'>
An unsigned mac binary is "damaged and can't be opened", so a release has
to be signed with a Developer ID and notarized by Apple, and that needs a
mac holding a private key. That mac is not the machine you develop on --
under `make all` it never is -- so `make macos-dist` builds nothing here:
it syncs the tree to the builder over ssh, has it build, sign, notarize
and staple, verifies the result there and fetches the zip back. The near
end needs only ssh, rsync and a hasher, so it works from Linux.

contrib/macos-builder-setup turns a bare mac into that builder and is
re-runnable, with --check to say what is missing. The two things that
make an unattended builder different from a laptop are handled rather
than worked around: a keychain of its own, unlocked from a 0600 password
file because a headless mac has no unlocked login keychain and codesign
dies with "User interaction is not allowed"; and exactly one copy of the
identity in the search list, because two make codesign ambiguous.

No identity is in the repo. Who signs, which team, which mac -- that is
Makefile.macos, which stays out of git along with dist/, and macos-dist
tells you so when it is not there.
</content>
</entry>
<entry>
<title>homebrew: a formula, and a script that keeps it honest</title>
<updated>2026-08-26T21:30:58Z</updated>
<author>
<name>Peter Geissler</name>
<email>peter@haxx.in</email>
</author>
<published>2026-08-26T21:29:54Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=91526fe1f3482f610b38caa214a91f78564f7d50'/>
<id>urn:sha1:91526fe1f3482f610b38caa214a91f78564f7d50</id>
<content type='text'>
`brew install slosh` is the shortest install there is, and the formula is
small because `make install` already does the work: zig as the only build
dependency, both zig caches redirected into the build directory so the
sandbox does not deny them and the user's real cache is not polluted, and
the commit id handed to the Makefile because a snapshot tarball has no
.git for `git describe` to read.

The url/version/sha256 triple is the part that rots. contrib/brew-release
resolves a ref to a full commit id, fetches the cgit snapshot, hashes what
it actually got, and rewrites those three stanzas -- it does not commit or
push, so the diff is read by a human first. Untagged pins carry the commit
count so `brew upgrade` can tell newer from older; alphabetical comparison
of two short ids cannot.

The formula lives here as the source of truth and is copied into the tap;
contrib/homebrew/README.md says why it is a formula and not a cask, and
what still stands between this and homebrew-core.
</content>
</entry>
</feed>
