<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Software · Nick Allevato</title><link>https://www.allevato.io/software/</link><description>Releases of the software on Nick Allevato</description><generator>Hugo</generator><language>en-us</language><lastBuildDate>Sat, 24 Oct 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.allevato.io/software/index.xml" rel="self" type="application/rss+xml"/><item><title>wr64-decomp</title><link>https://www.allevato.io/software/wr64-decomp/</link><pubDate>Sat, 24 Oct 2026 00:00:00 +0000</pubDate><guid isPermaLink="false">https://www.allevato.io/software/wr64-decomp/</guid><category>software</category><description>&lt;p>Wave Race 64 turned back into C that compiles, with Nintendo&amp;rsquo;s own compiler, to the exact same ROM.&lt;/p>&lt;pre>&lt;code>make toolchain &amp;amp;&amp;amp; make init # with your own US Rev 1 ROM as baserom.us.rev1.z64&lt;/code>&lt;/pre>&lt;h3 id="what-it-is">What it is&lt;/h3>
&lt;p>Wave Race 64 shipped in 1996 as a compiled MIPS binary. This turns the binary back into C source, and that source, compiled with the same compiler Nintendo used (IDO 5.3), produces &lt;strong>the same ROM down to the last byte&lt;/strong>. Every build ends with a SHA-1 check against the cartridge, so one wrong instruction fails it.&lt;/p>
&lt;p>It&amp;rsquo;s a fork of &lt;a href="https://github.com/LLONSIT/Wave-Race-64">LLONSIT/Wave-Race-64&lt;/a>, which did the groundwork: the ROM split, the build system and the first nine hundred or so functions. This fork is written with Claude, and upstream doesn&amp;rsquo;t take AI-generated code, so it runs independently and nothing goes back.&lt;/p>
&lt;h3 id="where-it-stands">Where it stands&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>Area&lt;/th>
 &lt;th>What&amp;rsquo;s in it&lt;/th>
 &lt;th style="text-align: right">Matched&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>libultra&lt;/code>&lt;/td>
 &lt;td>Nintendo&amp;rsquo;s N64 OS library&lt;/td>
 &lt;td style="text-align: right">157 / 157&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>other&lt;/td>
 &lt;td>handwritten assembly, loose segments&lt;/td>
 &lt;td style="text-align: right">55 / 55&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>audio&lt;/code>&lt;/td>
 &lt;td>sequence and sound playback&lt;/td>
 &lt;td style="text-align: right">227 / 228&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>overlays&lt;/code>&lt;/td>
 &lt;td>courses and menu screens&lt;/td>
 &lt;td style="text-align: right">148 / 151&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>codeseg&lt;/code>&lt;/td>
 &lt;td>HUD, menus, text, popups, fades&lt;/td>
 &lt;td style="text-align: right">210 / 219&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>sys&lt;/code>&lt;/td>
 &lt;td>boot, threads, memory&lt;/td>
 &lt;td style="text-align: right">35 / 37&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>game&lt;/code>&lt;/td>
 &lt;td>race logic, riders, camera, water&lt;/td>
 &lt;td style="text-align: right">470 / 501&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>total&lt;/strong>&lt;/td>
 &lt;td>&lt;/td>
 &lt;td style="text-align: right">&lt;strong>1,302 / 1,348&lt;/strong>&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>The functions that don&amp;rsquo;t match yet keep a readable C attempt beside the assembly, with a note on what still differs. &lt;code>make NON_MATCHING=1&lt;/code> builds with those, so most of the game can already be read and changed as C. The ROM differs then, which is expected.&lt;/p>
&lt;h3 id="what-its-for">What it&amp;rsquo;s for&lt;/h3>
&lt;p>A matching tree is the floor everything else stands on: how the water simulation really works, bug fixes, a native port, and mods well past what patching a binary allows. The one I want is in &lt;a href="https://www.allevato.io/2026/10/23/64-riders-in-wave-race-64/">64 riders in Wave Race 64&lt;/a>, which stopped short of growing the game&amp;rsquo;s four-rider arrays from the inside because the decomp wasn&amp;rsquo;t far enough along.&lt;/p>
&lt;p>After the bytes comes the half of the job the hash can&amp;rsquo;t judge: names, structs, documentation. &lt;strong>The hash will say when the bytes are right. Nothing will say when the names are.&lt;/strong> That argument is &lt;a href="https://www.allevato.io/2026/10/27/the-bytes-dont-remember-the-names/">the bytes don&amp;rsquo;t remember the names&lt;/a>.&lt;/p>
&lt;p>You need your own copy of the game, US Rev 1, &lt;code>.z64&lt;/code>. It isn&amp;rsquo;t in the repo and never will be.&lt;/p></description></item><item><title>Mission: Impossible Recomp 0.1.0</title><link>https://www.allevato.io/software/mi-recomp/</link><pubDate>Wed, 21 Oct 2026 00:00:00 +0000</pubDate><guid isPermaLink="false">https://www.allevato.io/software/mi-recomp/#v0.1.0</guid><category>software</category><description>&lt;p>The 1998 N64 &lt;em>Mission: Impossible&lt;/em>, statically recompiled into a native Linux program: 60 FPS, widescreen at any resolution, keyboard and mouse.&lt;/p>&lt;pre>&lt;code>Releases → the AppImage → chmod +x, run, pick your ROM on first start&lt;/code>&lt;/pre>&lt;h3 id="what-it-is">What it is&lt;/h3>
&lt;p>This isn&amp;rsquo;t an emulator. All 1,697 of the game&amp;rsquo;s MIPS functions are translated to C ahead of time by N64Recomp, compiled into an ordinary executable, and run on N64ModernRuntime, with RT64 doing the drawing. Before this the game had no decomp, no recomp and no PC port.&lt;/p>
&lt;p>It went from first commit to all twenty levels loading and running at 59 to 61 FPS in under five hours. The next morning added smooth frame pacing, crash fixes and the first level mod.&lt;/p>
&lt;h3 id="what-it-adds">What it adds&lt;/h3>
&lt;ul>
&lt;li>&lt;strong>60 FPS&lt;/strong> without speeding the game up. Game logic runs on the video clock, and a hook interpolates root motion so walking stays smooth between the 30 Hz keyframes.&lt;/li>
&lt;li>&lt;strong>Keyboard and mouse.&lt;/strong> WASD, mouse-turn, right-click to aim. The mouse also drives the sniper scope and the gunboat turret.&lt;/li>
&lt;li>&lt;strong>Analog camera&lt;/strong> on the right stick.&lt;/li>
&lt;li>&lt;strong>Level tooling.&lt;/strong> The &amp;ldquo;Burp&amp;rdquo; archive format and level table are decoded, any level exports to OBJ with textures, and edited level data loads at runtime.&lt;/li>
&lt;/ul>
&lt;h3 id="what-you-need">What you need&lt;/h3>
&lt;p>Linux with Vulkan, and your own copy of the cartridge as a &lt;code>.z64&lt;/code>. The AppImage is one file; there&amp;rsquo;s a &lt;code>.tar.gz&lt;/code> beside it if you&amp;rsquo;d rather. The release is built on Ubuntu 22.04, so most current distros run it. Building from source takes clang and a handful of tools the README lists.&lt;/p>
&lt;p>Claude did the reverse engineering and I played it. The README says so and doesn&amp;rsquo;t apologise.&lt;/p></description></item><item><title>evo-sim</title><link>https://www.allevato.io/software/evo-sim/</link><pubDate>Wed, 07 Oct 2026 00:00:00 +0000</pubDate><guid isPermaLink="false">https://www.allevato.io/software/evo-sim/</guid><category>software</category><description>&lt;p>An open, numerical audit of Vox Day&amp;rsquo;s &lt;em>Probability Zero&lt;/em> argument and of his critics, where every number reruns. The groundwork for a simulator you can tune yourself.&lt;/p>&lt;pre>&lt;code>python3 -m venv research/.venv &amp;amp;&amp;amp; research/.venv/bin/pip install -r research/requirements.txt matplotlib
research/.venv/bin/python -I research/checks/baseline_textbook.py # must pass before any check&lt;/code>&lt;/pre>&lt;h3 id="what-it-is">What it is&lt;/h3>
&lt;p>Vox Day argues that evolution can&amp;rsquo;t fix enough mutations in the time available. His critics say his math is wrong. Both sides mostly argue in posts. evo-sim takes the argument apart into &lt;strong>234 claims, each with a verbatim quote, a formal statement, a prediction written down before the check, and three verdicts&lt;/strong>, then runs the checks: exact chains, forward simulation, coalescent models, real data where it exists.&lt;/p>
&lt;p>It scores both sides with the same rules. His empty-pipe formula is exact for its premise; the ancestral pipe was full, not empty, and he has since withdrawn that start. Both of those are on the same scorecard, at the same length.&lt;/p>
&lt;h3 id="what-you-can-do-with-it">What you can do with it&lt;/h3>
&lt;p>Rerun anything. Seeds are fixed, the textbook baselines have to pass before any check is allowed to run, and a lint ties every claim to the argument tree and to where it came from. If a number on this site is wrong, the repo is where to prove it, and corrections from either side are invited as issues with a locator.&lt;/p>
&lt;h3 id="what-it-isnt-yet">What it isn&amp;rsquo;t yet&lt;/h3>
&lt;p>&lt;strong>It&amp;rsquo;s an AI-run audit with no independent human review.&lt;/strong> All 65 reviews are by the same model family and none were blind. Of 57 checks, 10 were registered clearly before their results. The book itself hasn&amp;rsquo;t been read; the audit works from Day&amp;rsquo;s posts and papers. Nobody named in it has been shown the findings. The README leads with those gaps, in a table, before any result, and so does this page.&lt;/p>
&lt;p>The simulator you can tune is the stated next phase. Today it&amp;rsquo;s the audit that has to come first.&lt;/p></description></item><item><title>Chessbrief</title><link>https://www.allevato.io/software/chessbrief/</link><pubDate>Wed, 12 Aug 2026 00:00:00 +0000</pubDate><guid isPermaLink="false">https://www.allevato.io/software/chessbrief/</guid><category>software</category><description>&lt;p>Reads every game you have ever played on Chess.com and tells you the true things, in order of how well the evidence supports them.&lt;/p>&lt;p>&lt;a href="https://chessbrief.bmux.sh">open it &amp;#8599;&lt;/a>&lt;/p>&lt;h3 id="what-it-is">What it is&lt;/h3>
&lt;p>Point it at a Chess.com account and it pulls the whole archive — five thousand games across three and a half years, in the case above — and writes a briefing. Not a dashboard. A &lt;strong>briefing&lt;/strong>: a ranked list of claims about how you play, each one with the numbers that support it sitting directly underneath.&lt;/p>
&lt;p>The top of the page picks a format and says why it picked it: &lt;em>blitz leads the briefing because it is the format with the most games still being played.&lt;/em> That sentence is the whole design in miniature. Every choice the page makes about what to show you, it shows you.&lt;/p>
&lt;p>&lt;img src="https://www.allevato.io/images/projects/chessbrief-02.jpg" alt="Claim one, with the evidence under it: how your games end, wins against losses">&lt;/p>
&lt;h3 id="the-thing-worth-stealing">The thing worth stealing&lt;/h3>
&lt;p>Analytics tools almost always overclaim, because a confident sentence is more satisfying than an honest one. This one is built to refuse.&lt;/p>
&lt;p>Three habits do it:&lt;/p>
&lt;p>&lt;strong>Claims are ranked by evidence, not by drama.&lt;/strong> The heading above the first one literally reads &lt;em>strongest evidence on the page&lt;/em>. The interesting-sounding finding does not get to go first just because it is interesting.&lt;/p>
&lt;p>&lt;strong>Intervals, not points.&lt;/strong> A win rate arrives as 48.5% with a 95% interval of 47.0–50.1%, so the reader can see when a difference is too small to mean anything.&lt;/p>
&lt;p>&lt;strong>It argues against its own framing.&lt;/strong> The page notes that a peak is the maximum of a noisy series — so part of any gap between where you are and your best number is just the arithmetic of maxima rather than ability you have lost. Then it does the thing almost nothing does: &lt;strong>shows the gap, and declines to call it decline.&lt;/strong>&lt;/p>
&lt;p>That last one is a tool deliberately giving up its most emotionally effective sentence because the sentence would not be true. Every rating graph ever drawn invites &amp;ldquo;you&amp;rsquo;re getting worse.&amp;rdquo; This one puts up the number and refuses the story.&lt;/p>
&lt;h3 id="what-it-actually-finds">What it actually finds&lt;/h3>
&lt;p>The claims are concrete and slightly uncomfortable, which is the point. The example above: 59% of recorded losses end in resignation, against only 33% of wins ending with the opponent resigning. Stated plainly — &lt;strong>you concede earlier than your opponents do&lt;/strong> — and backed with the full breakdown of how games end, wins against losses, including the lopsided ones. Timeouts run 2.8× more wins than losses; abandons are 4 against 207.&lt;/p>
&lt;p>None of that is a tip. It is a description, with the arithmetic attached, and it leaves the conclusion to you.&lt;/p>
&lt;h3 id="technical-details">Technical details&lt;/h3>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>&lt;/th>
 &lt;th>&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;strong>Frontend&lt;/strong>&lt;/td>
 &lt;td>Next.js, React&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Data&lt;/strong>&lt;/td>
 &lt;td>Chess.com public API — full game archive, synced on load&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Scale&lt;/strong>&lt;/td>
 &lt;td>~5,000 games, 3.5 years, per-format breakdowns&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Statistics&lt;/strong>&lt;/td>
 &lt;td>95% confidence intervals; explicit noise caveats on peaks&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;strong>Structure&lt;/strong>&lt;/td>
 &lt;td>Ranked claims, each with its own evidence block&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table></description></item><item><title>Tickler 0.20.4</title><link>https://www.allevato.io/software/tickler/</link><pubDate>Wed, 12 Aug 2026 00:00:00 +0000</pubDate><guid isPermaLink="false">https://www.allevato.io/software/tickler/#v0.20.4</guid><category>software</category><description>&lt;p>A Paperclip plugin that puts every organization you run on one page, with one queue of what needs you, sorted by when you have to decide.&lt;/p>&lt;pre>&lt;code>Settings → Plugins → Install Plugin → paperclip-plugin-tickler&lt;/code>&lt;/pre>&lt;h3 id="what-it-is">What it is&lt;/h3>
&lt;p>&lt;a href="https://github.com/paperclipai/paperclip">Paperclip&lt;/a> is an open-source control plane for running agents as a company — roles, budgets, issues, heartbeats — and a single deployment runs many companies with complete isolation between them. That isolation is correct for the agents. Company A&amp;rsquo;s agents have no business seeing company B. But the operator is not an agent. The operator is the one person all those companies have in common, and the product opens you inside one of them with a switcher for the rest.&lt;/p>
&lt;p>Tickler is the missing floor. It belongs to no company, sits above the boundary, and answers one question: &lt;em>what needs me, across every org, right now?&lt;/em>&lt;/p>
&lt;p>It is a third-party plugin, not a Paperclip feature and not an official Paperclip project. It&amp;rsquo;s UI-only — it never modifies Paperclip itself, so upgrades don&amp;rsquo;t break it.&lt;/p>
&lt;h3 id="what-it-does">What it does&lt;/h3>
&lt;img src="https://www.allevato.io/images/tickler/orgs-rail.png" class="gallery-right" alt="The left rail: one line per org with capacity squares, hourly token bars and a need-you count, then Recent, Portfolio and Routines">
&lt;p>&lt;strong>Every org on one line.&lt;/strong> Who&amp;rsquo;s working, a bar per hour of tokens spent, and the count of things waiting on you. That last column is the one no tool shows and the one that should decide where you go next. The token bars share one scale, so the org quietly burning through its budget at 3 a.m. stands out at a glance.&lt;/p>
&lt;p>&lt;strong>Answer in place.&lt;/strong> Approve, reject, reply, or pick an option on an agent&amp;rsquo;s question right on the queue row. You don&amp;rsquo;t open the item, you don&amp;rsquo;t enter the company, you don&amp;rsquo;t lose your place. Reject takes two taps, because a rejected approval can&amp;rsquo;t be undone and on a phone it sits a thumb&amp;rsquo;s width from Open.&lt;/p>
&lt;p>&lt;strong>Routines you can fire from here.&lt;/strong> Broken routines surface on their own. Pin any other routine and it stays at the top of the pane with a ▶ that runs it — press twice — and links the issue the run made.&lt;/p>
&lt;div class="gallery-clear">&lt;/div>
&lt;img src="https://www.allevato.io/images/tickler/queue-by-decide.png" class="gallery-left" alt="The queue grouped into Today, Unsorted, This week, Alerts, Whenever and Snoozed">
&lt;p>&lt;strong>Decide by.&lt;/strong> Mark each item Today, This week or Whenever, and the queue sorts itself into those buckets. It writes through Paperclip&amp;rsquo;s own decision triage, so the dates show up inside Paperclip too — this isn&amp;rsquo;t a private layer of sticky notes.&lt;/p>
&lt;p>&lt;strong>Trouble surfaces itself.&lt;/strong> Blocked projects, stalled runs, failed routines and overdue heartbeats rise to the top on their own. So do runs that have outlived their agent&amp;rsquo;s time limit: Paperclip has given up on those, nothing will ever close them, and they hold the agent&amp;rsquo;s run slot until somebody notices. Hovering an agent or a task shows its live narration, so &amp;ldquo;what is this thing actually doing&amp;rdquo; is a mouse move rather than four clicks.&lt;/p>
&lt;p>&lt;strong>Demo mode.&lt;/strong> &lt;code>?demo=1&lt;/code> swaps in invented orgs, which is how every screenshot on this page was taken.&lt;/p>
&lt;div class="gallery-clear">&lt;/div>
&lt;img src="https://www.allevato.io/images/tickler/phone.png" class="gallery-right" alt="The same page stacked at phone width" style="width: 12%;">
&lt;p>&lt;strong>It works on a phone&lt;/strong>, which matters more than it sounds: the moment you most need to know what&amp;rsquo;s waiting is usually the moment you&amp;rsquo;re not at the desk.&lt;/p>
&lt;div class="gallery-clear">&lt;/div>
&lt;h3 id="how-it-got-here">How it got here&lt;/h3>
&lt;p>The problem came long before the plugin. I&amp;rsquo;d been circling it for years: the cost of crossing between organizations, and the fact that no tool ever absorbs it for you. Paperclip was the first place one person natively owns several organizations, so it was the first place the fix could actually be built.&lt;/p>
&lt;p>The prototype shipped as Plica, Latin for &amp;ldquo;fold&amp;rdquo;. Nice idea. Nobody could guess what it meant. Version 0.7.0, shipped 27 September 2026, renamed everything — npm package, plugin id, route, CSS classes, repo — to Tickler. A &lt;em>tickler file&lt;/em> is the office system that predates all of this: an accordion of dated folders whose only job is to bring a thing back in front of you on the day it needs you. That&amp;rsquo;s exactly what the decide-by queue does. The rename itself turned into a story about Latin roots and find-and-replace, told in &lt;a href="https://www.allevato.io/posts/name-it-after-the-paper/">name it after the paper&lt;/a>.&lt;/p>
&lt;p>&lt;strong>If you were running Plica, uninstall it and install Tickler.&lt;/strong> Paperclip identifies plugins by id, so a new id is a new plugin, and you&amp;rsquo;ll need to re-enter your token thresholds. With an install base that small, a clean break beat a migration layer.&lt;/p>
&lt;h3 id="three-weeks-out">Three weeks out&lt;/h3>
&lt;p>Thirty-four releases in the three weeks since the rename, which is what happens when the thing you built is the thing you use all day.&lt;/p>
&lt;p>npm counts about 4,700 downloads since launch, a little over 3,000 of them in the week of 5 October. I&amp;rsquo;d take both numbers with salt. A download is an &lt;code>npm install&lt;/code>, not a person, and Tickler has an update button that runs one every time somebody takes a new version — so shipping 34 releases inflates the count all by itself. The better signal is quieter: somebody I&amp;rsquo;ve never met has been filing issues on the repo, and four of the releases since are things they reported or asked for.&lt;/p>
&lt;p>A few of the releases I&amp;rsquo;d point at:&lt;/p>
&lt;ul>
&lt;li>&lt;strong>It updates itself, and it can undo that.&lt;/strong> 0.8.0 added an update button, because Paperclip&amp;rsquo;s Plugin Manager has none, and uninstalling to upgrade throws away the plugin&amp;rsquo;s config. 0.14.3 made the button wait until npm can actually serve the new version — for about five minutes after a publish, npm lists it as latest and still 404s the tarball. 0.17.0 went further: Tickler remembers which versions drew the page without an error in your browser, and when a new one crashes, the crash notice offers &lt;strong>Roll back to&lt;/strong> the last good one, reinstalled from npm in place.&lt;/li>
&lt;li>&lt;strong>A crash costs only the part that broke.&lt;/strong> Before 0.15.1, any error took the whole page down to Paperclip&amp;rsquo;s catch-all, &amp;ldquo;failed to render&amp;rdquo;, with the reason buried in the console. Now one org with bad data loses only its own line, a failing pane loses only that pane, and each notice carries the error, a &lt;strong>Try again&lt;/strong>, and a &lt;strong>Report on GitHub&lt;/strong> that pre-fills the issue.&lt;/li>
&lt;li>&lt;strong>You can hide an org without archiving it.&lt;/strong> 0.19.0. A hidden org is dropped before Tickler asks the server for anything, so it costs nothing, and it keeps running in Paperclip exactly as before.&lt;/li>
&lt;li>&lt;strong>It admits what it is.&lt;/strong> 0.10.0 put a &lt;code>?&lt;/code> in the header saying Tickler is a community plugin, not an officially supported part of Paperclip. Somebody hitting a rough edge had no way to tell which of the two projects to be annoyed at, and the nearest obvious target was Paperclip&amp;rsquo;s issue tracker, where nobody who can fix Tickler is reading.&lt;/li>
&lt;/ul>
&lt;p>Most of the rest went into the left rail, which turned out to be the hardest thing in the plugin: a fixed height and four panes that each want more of it. The rail measures itself and hands each pane a header plus a whole number of rows, and Orgs or Recent can each be expanded at the cost of folding the panes beneath. Getting that to settle took more than one try. Twice the rail argued with itself until React gave up and stopped drawing the page. The better of the two: in full-screen mode, a pane&amp;rsquo;s header wrapped onto a second line, which changed the budget, which made its &amp;ldquo;+N more&amp;rdquo; chip disappear, which unwrapped the header, which brought the chip back.&lt;/p>
&lt;p>The test I&amp;rsquo;d defend hardest is the least visible: a &lt;code>check:scroll&lt;/code> sweep that drives the board at 32 window sizes in a real browser and fails any pane that offers a scrollbar while claiming to show everything. Two early releases went out with a pane scrolling over a single rounded pixel. Measuring with &lt;code>offsetHeight&lt;/code> — an integer — against rows that are 28.297px tall will do that, and you cannot see it by looking.&lt;/p>
&lt;h3 id="why-it-exists">Why it exists&lt;/h3>
&lt;p>I keep circling a thing I call the multi-organization problem: when your attention is split across several organizations, every crossing between them costs something, and almost no tool absorbs that cost for you. Every tool hands you a switcher — Slack&amp;rsquo;s workspace list, GitHub&amp;rsquo;s account menu, Chrome profiles — and a switcher assumes you already know where you&amp;rsquo;re going. Most of the time I don&amp;rsquo;t.&lt;/p>
&lt;p>That&amp;rsquo;s also why a vendor will never build this for you: the vendor&amp;rsquo;s unit is the organization, and the problem lives between them. The argument is in &lt;a href="https://www.allevato.io/articles/a-root-to-stand-on/">a root to stand on&lt;/a>; the ten-year version is &lt;a href="https://www.allevato.io/articles/the-multi-organization-problem/">the multi-organization problem&lt;/a>.&lt;/p></description></item><item><title>zsh_custom</title><link>https://www.allevato.io/software/zsh-custom/</link><pubDate>Fri, 30 Jan 2026 00:00:00 +0000</pubDate><guid isPermaLink="false">https://www.allevato.io/software/zsh-custom/</guid><category>software</category><description>&lt;p>A shell suite that makes moving between sixty-nine projects cost one keystroke, and coming back after a reboot cost nothing at all.&lt;/p>&lt;h3 id="what-it-is">What it is&lt;/h3>
&lt;p>Ten zsh files that load in numeric order, about 3,650 lines, sitting inside the home lab toolkit. The entire thing exists to answer one question faster than you can finish asking it: &lt;strong>which project am I in, and what was I doing in it?&lt;/strong>&lt;/p>
&lt;p>A single &lt;code>project_env/projects.json&lt;/code> is the manifest of record — sixty-nine projects, each with an id, a display name, a repo, a canonical path, a colour, a category and a sort order. It is committed, so registering a project registers it on every machine. Everything else reads from there.&lt;/p>
&lt;p>&lt;code>cs&lt;/code> opens the fuzzy menu. Pick a project and the shell becomes that project: the working directory, the environment, its own &lt;code>.aliases&lt;/code>, its SSH shortcuts, and the terminal background taking a hint of the project&amp;rsquo;s colour so a glance at the window tells you where you are. The context survives a shell restart, because the one thing worse than switching is switching and then having to remember.&lt;/p>
&lt;p>Right arrow walks a carousel — projects, then hosts, then the client category, then back to projects — so every menu is reachable by counting presses rather than remembering a command.&lt;/p>
&lt;h3 id="the-part-id-keep">The part I&amp;rsquo;d keep&lt;/h3>
&lt;p>The session registry, which is a fifth of the whole suite and the only part I&amp;rsquo;d call a design rather than a convenience.&lt;/p>
&lt;p>A hook writes one record per Claude Code session, at start and at end. The interesting decision is that &lt;strong>a session&amp;rsquo;s state is never stored — it is worked out at read time&lt;/strong>, from whether an end was recorded, which boot it happened under, and whether the pid is still a running process:&lt;/p>
&lt;table>
 &lt;thead>
 &lt;tr>
 &lt;th>&lt;/th>
 &lt;th>&lt;/th>
 &lt;/tr>
 &lt;/thead>
 &lt;tbody>
 &lt;tr>
 &lt;td>&lt;code>live&lt;/code>&lt;/td>
 &lt;td>No end recorded, same boot, pid still running&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>ended&lt;/code>&lt;/td>
 &lt;td>End fired — closed on purpose&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>dead&lt;/code>&lt;/td>
 &lt;td>No end, same boot, pid gone — killed, OOM&amp;rsquo;d or crashed&lt;/td>
 &lt;/tr>
 &lt;tr>
 &lt;td>&lt;code>rebooted&lt;/code>&lt;/td>
 &lt;td>No end, recorded under a &lt;em>different&lt;/em> boot — the machine went down underneath it&lt;/td>
 &lt;/tr>
 &lt;/tbody>
&lt;/table>
&lt;p>Only the last of those is a disaster, and it is the one &lt;code>crestore&lt;/code> fixes: it reopens everything that was running when the box went down. The distinction matters because a stored flag would have been written by the process that died, which is exactly the process that cannot be trusted to write it.&lt;/p>
&lt;p>That design came out of a measurement rather than a theory. The obvious assumption is that closing a terminal kills a session silently — it does not. &lt;strong>Closing the terminal sends SIGHUP, which does fire the end hook.&lt;/strong> Tested, dated, and written into the source as a comment, because the next person to read it will assume otherwise.&lt;/p>
&lt;h3 id="the-small-one">The small one&lt;/h3>
&lt;p>&lt;code>ssh&lt;/code> is wrapped. Modern terminals export exotic &lt;code>TERM&lt;/code> values — &lt;code>xterm-ghostty&lt;/code>, &lt;code>kitty&lt;/code> — and a host with an older ncurses answers with &lt;em>unknown terminal type&lt;/em> and a dead session. The wrapper downgrades &lt;code>TERM&lt;/code> to &lt;code>xterm-256color&lt;/code> for the remote side only, local terminal untouched. One environment variable turns it off.&lt;/p>
&lt;p>It is four lines of real work and it removed a class of papercut that had been costing a minute at a time for a year. Most of this suite is that: &lt;strong>not clever, just finally written down.&lt;/strong>&lt;/p>
&lt;h3 id="why-it-exists">Why it exists&lt;/h3>
&lt;p>I keep circling the cost of crossing between contexts. &lt;a href="https://www.allevato.io/software/tickler/">Tickler&lt;/a> is that argument in a browser — every organization on one page, one queue of what needs you. This is the same argument in a terminal, and it came first.&lt;/p>
&lt;p>The claim in both is the same: a switcher assumes you already know where you are going, and the expensive part was never the switch. It was the reconstruction on the other side — which window, which host, which session, what was I in the middle of. The long version is &lt;a href="https://www.allevato.io/articles/the-multi-organization-problem/">the multi-organization problem&lt;/a>; the shorter one is &lt;a href="https://www.allevato.io/articles/a-root-to-stand-on/">a root to stand on&lt;/a>.&lt;/p></description></item></channel></rss>