<feed xmlns='http://www.w3.org/2005/Atom'>
<title>slosh.git/.fastfeedback/SPEED.md, branch master</title>
<subtitle>an opinionated terminal multiplexer in C with programmable pane chrome</subtitle>
<id>https://git.sl0p.foo/slosh.git/atom/.fastfeedback/SPEED.md?h=master</id>
<link rel='self' href='https://git.sl0p.foo/slosh.git/atom/.fastfeedback/SPEED.md?h=master'/>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/'/>
<updated>2026-08-28T13:38:41Z</updated>
<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>slosh: rename the project from sl0ppty</title>
<updated>2026-08-18T19:26:00Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-18T19:26:00Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=b9e53cace4eb75263077df8623b011e66f5f2241'/>
<id>urn:sha1:b9e53cace4eb75263077df8623b011e66f5f2241</id>
<content type='text'>
sl0p + pty was error-prone to type and the zero made it worse; slosh
(slop + sh) is one syllable, impossible to typo, and slosh.foo is ours.

Mechanical rename everywhere it appears: binary, env vars (SLOSH,
SLOSH_SESSION, SLOSH_BIN, SLOSH_CONFIG), socket dir (/tmp/slosh-&lt;uid&gt;,
$XDG_RUNTIME_DIR/slosh), config path (~/.config/slosh), header guards,
src/sl0ppty.h -&gt; src/slosh.h, contrib/sl0ppty-dev -&gt; contrib/slosh-dev,
the driving-slosh skill, docs and tests. The sl0ppi fork references and
the sl0p theme stay: one is history, the other is the brand.

The rename surfaced a latent bug in test_osc5577: the hello-reply
answer always appeared on screen twice (ECHOCTL echo plus cat -v), but
a seven-character implementation name happened to wrap the second copy
across the 60-column boundary, so counting substrings in wrapped text
found one and passed for the wrong reason. A five-character name moved
the wrap and exposed it. The loop guard itself is correct -- with echo
off before the request there is exactly one reply -- so the test now
counts on de-wrapped text and tolerates the pane's own echo.
</content>
</entry>
<entry>
<title>make coverage: a number, cheaply</title>
<updated>2026-08-15T14:07:32Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-15T14:07:32Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=5585c0736e876c9c54ec4feb5139a8607139daa7'/>
<id>urn:sha1:5585c0736e876c9c54ec4feb5139a8607139daa7</id>
<content type='text'>
`make coverage` builds a second binary with gcc's --coverage into build/cov,
runs every suite against it, and prints a per-file table from gcov. About a
minute. Nothing installed, nothing about the normal zig build changed.

gcc rather than clang because the counters need a gcov that matches the
compiler, and gcov is what this machine has -- no llvm-profdata, no llvm-cov,
no lcov, no gcovr. The aggregation is forty lines of python reading gcov's own
summaries, which is the whole reason this was cheap.

The suites drive it through $SL0PPTY_BIN, one line in the harness, so there is
no second copy of the test code and no risk of the coverage run testing
something else.

Two things about the result, both in the report itself:

- the C unit tests link the *same* instrumented objects, so input.c, kdl.c,
  shader.c and expr.c are credited for the tests that exercise them. Built
  separately, input.c read 54% while having a dedicated decoder test -- a lie
  about where the tests are rather than about the code.
- server.c reads ~8% and is not untested. The session server is a daemonised
  child ending in _exit(), which is right there and skips the flush gcov needs.
  The counters are lost, not the tests. Said in the output, because an
  unexplained low number invites someone to "fix" working code -- and changing
  correct code to make a tool happier is the wrong trade.

Where it lands today: 85.4% of 6298 lines. The gaps that are real rather than
mechanical are jsonval.c (58%), screen.c (64%) and pty.c (59%) -- error paths
and the parts of the diff emitter that only a resize or a weird terminal
reaches.
</content>
</entry>
<entry>
<title>sl0ppty: the test cycle was 80s, it is now 5.5s</title>
<updated>2026-08-14T13:16:06Z</updated>
<author>
<name>user</name>
<email>user@clank</email>
</author>
<published>2026-08-14T13:16:06Z</published>
<link rel='alternate' type='text/html' href='https://git.sl0p.foo/slosh.git/commit/?id=c20b226d44af64ac476173361dad9676dbc74043'/>
<id>urn:sha1:c20b226d44af64ac476173361dad9676dbc74043</id>
<content type='text'>
Measured before changing anything, because the obvious suspect was wrong: the
build is not slow. zig caches objects, so even a clean rebuild is ~0.15s and an
incremental one is 0.08s. The C unit tests are 0.00s. All eighty seconds were
in the python suite, and almost all of that was waiting on durations that were
guesses.

Four things, in order of what they bought.

Run the python tests in parallel. They are separate processes that share
nothing — script mode opens no socket — so each is now its own make target with
a stamp, and `make -j` runs them at once. 78s -&gt; 21s for free. The stamps are
worth as much as the parallelism day to day: an untouched test whose binary has
not changed does not run again, so `make test` after editing one test file is
1.1s and after editing nothing is 0.02s. The binary is a prerequisite of every
stamp, so changing the code still re-runs everything.

Then stop guessing at durations. `settle N` is the only thing that reads a
pane's pty, so it is how you wait for a program's output — but `send` and
`snapshot` are already synchronous with the app, which means a settle after a
mouse event, waiting to assert on our own chrome, is waiting for nothing at
all. Those are removed. Where a program's output really is the thing being
waited for, harness.until()/until_text() polls in 10ms steps and returns the
instant it lands: measured, that is one poll, against the 200-300ms those calls
used to donate. The default settle drops 120ms -&gt; 20ms on the same reasoning.

Three effects are gated on a real clock rather than on anything observable —
the hover dwell, and a toast's lifetime. Those genuinely have to wait, so the
tests that are not *about* the timing now configure the clock down instead
(hover_delay_ms 20, toast_ms 150) and the two tests that are about it keep the
real defaults, because a dwell test that cannot observe not-yet-elapsed proves
nothing.

Two tests turned out to be passing by accident, which is the useful kind of
thing a speedup shakes loose: both asserted the resize hint was gone after a
drop while the pointer was still sitting on the handle. It was only gone
because the dwell had not re-elapsed — resting on a handle *should* re-arm the
hint. They now move off the handle first and assert the thing they meant.

Unrelated but found on the way: the Makefile tracked no header dependencies, so
editing a .h rebuilt nothing and the only safe move was `make clean`. -MMD -MP
fixes it; touching shader.h now rebuilds exactly the six objects that include
it. That one was a correctness bug wearing a performance costume.

.fastfeedback/SPEED.md records the measurements and how to write a fast test
here, so the next person does not rediscover that settle-after-a-click is free
to delete.
</content>
</entry>
</feed>
