<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Herddeck on Nick Liu - Software Engineer</title><link>https://nick-liu.com/tags/herddeck/</link><description>Recent content in Herddeck on Nick Liu - Software Engineer</description><generator>Hugo -- gohugo.io</generator><language>en</language><managingEditor>nickboy@users.noreply.github.com (Nick Liu)</managingEditor><webMaster>nickboy@users.noreply.github.com (Nick Liu)</webMaster><copyright>2026 Nick Liu</copyright><lastBuildDate>Sun, 30 Aug 2026 00:52:14 -0700</lastBuildDate><atom:link href="https://nick-liu.com/tags/herddeck/index.xml" rel="self" type="application/rss+xml"/><item><title>The context percentage is a division. Nothing tells you the denominator.</title><link>https://nick-liu.com/posts/context-window-denominator/</link><pubDate>Mon, 07 Sep 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/context-window-denominator/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;Showing "this session is 34% full" requires two numbers. The token count is easy and always right. The window size it divides by is not in the transcript, not in the session state files, not in settings, and not in any hook payload. It exists in exactly one place, and if you cannot read that place you are guessing at the denominator of a number you are about to display as fact.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;Claude Code&lt;/strong&gt; &lt;code&gt;2.1.x&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;I built a script that reports how full a Claude Code session&amp;rsquo;s context window is, so a Stream Deck key could draw a ring for it. The token side was straightforward. The denominator turned into the whole project.&lt;/p&gt;</description><content:encoded>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;Showing "this session is 34% full" requires two numbers. The token count is easy and always right. The window size it divides by is not in the transcript, not in the session state files, not in settings, and not in any hook payload. It exists in exactly one place, and if you cannot read that place you are guessing at the denominator of a number you are about to display as fact.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;Claude Code&lt;/strong&gt; &lt;code&gt;2.1.x&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;I built a script that reports how full a Claude Code session&amp;rsquo;s context window is, so a Stream Deck key could draw a ring for it. The token side was straightforward. The denominator turned into the whole project.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Five places the window size is not&#10; &lt;div id="five-places-the-window-size-is-not" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#five-places-the-window-size-is-not" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;I checked each of these against a live session rather than reasoning about what ought to be there.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Source&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Has the window size?&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;statusline payload&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;yes&lt;/strong&gt;, as &lt;code&gt;context_window.context_window_size&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;transcript &lt;code&gt;.jsonl&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;no. &lt;code&gt;message.model&lt;/code&gt; drops the &lt;code&gt;[1m]&lt;/code&gt; long-context marker&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;~/.claude/sessions/&amp;lt;pid&amp;gt;.json&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;no&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;~/.claude/settings.json&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;only the &lt;em&gt;default&lt;/em&gt; model, which is wrong after a &lt;code&gt;/model&lt;/code&gt; switch&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;hook payloads&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;no, and the hooks documentation says so&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;The transcript row is the one that stings, because the transcript is otherwise a perfect source: it is on disk, Claude Code writes it without being asked, and reading it changes nothing. It carries &lt;code&gt;message.model&lt;/code&gt;. What it does not carry is whether that session is running the long-context variant, because the marker is dropped from the model string it records.&lt;/p&gt;&#10;&lt;p&gt;The transcript can tell you which model. It can never tell you which window.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Why I rejected a model-to-window lookup table&#10; &lt;div id="why-i-rejected-a-model-to-window-lookup-table" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-i-rejected-a-model-to-window-lookup-table" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;The obvious fix is a table: model name in, window size out. I decided against it, and not because tables are inelegant.&lt;/p&gt;&#10;&lt;p&gt;A lookup table rots on every model release, and it rots in the bad direction. It does not throw. It does not warn. A model it has never heard of falls through to whatever default the author picked, and the tool goes on confidently reporting a percentage computed against the wrong denominator. Being silently wrong about a percentage is worse than showing nothing, because a number on a screen gets read as a measurement.&lt;/p&gt;&#10;&lt;p&gt;There is a real API answer here and it is worth stating precisely, because it is &lt;em&gt;almost&lt;/em&gt; the fix. Anthropic&amp;rsquo;s Models API returns &lt;code&gt;max_input_tokens&lt;/code&gt; per model id: machine-readable, and it does not rot. What it cannot do is disambiguate a variant that is not a distinct model id. &lt;code&gt;[1m]&lt;/code&gt; is a session-level marker rather than something you can look up, and it is the exact thing the transcript drops. So you would be querying the right API with a key that has already lost the distinction you care about, and getting a confident answer to a question you did not ask.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;What the script does instead&#10; &lt;div id="what-the-script-does-instead" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-the-script-does-instead" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;One assumption, named, in one place, with an override and a clamp:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# The one assumption in this script. Not a model-&amp;gt;window table on purpose:&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# such a table rots on every model release, and being silently wrong about&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# a percentage is worse than not showing one.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;CONTEXT_WINDOW_TOKENS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;HERDDECK_CONTEXT_WINDOW&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Three properties of that shape matter more than the value itself.&lt;/p&gt;&#10;&lt;p&gt;It is one constant rather than a table, so there is exactly one thing to be wrong about and one thing to change. It takes an environment override, so a machine running a different window fixes itself without a code change. And the overrun clamps:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pct&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tokens&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;CONTEXT_WINDOW_TOKENS&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;That clamp does real work. Without it, a session in a larger window than the constant assumes draws a ring at 216%, which is less a display bug than a confession. Clamping to 100 turns &amp;ldquo;my assumption is wrong&amp;rdquo; into &amp;ldquo;this session is full&amp;rdquo;, which is at least the right shape of wrong.&lt;/p&gt;&#10;&lt;p&gt;The deeper point is that only the &lt;em&gt;final division&lt;/em&gt; depends on the guess. The token count, which is what the script actually derives from the transcript, is always correct. Show tokens and you are reporting. Show a percentage and you are asserting.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Two routes, and why both exist&#10; &lt;div id="two-routes-and-why-both-exist" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#two-routes-and-why-both-exist" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;There are two ways to get this number and they are not equivalent:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;statusline&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;transcript scan&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Percentage&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;exact, computed by Claude Code&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;needs the window constant&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Coverage&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;every session&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;needed two fallbacks to match&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Cost&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;none&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;one polling process&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Requires&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;editing that machine&amp;rsquo;s statusline&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;nothing&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;The statusline route wins wherever the statusline is yours to edit, because Claude Code resolves the window itself and hands over an already-computed &lt;code&gt;used_percentage&lt;/code&gt;. Reading a number somebody else computed correctly is the only approach here that cannot rot.&lt;/p&gt;&#10;&lt;p&gt;The scan exists for machines where the statusline is not yours. A company-managed statusline you must not touch is a real constraint, and the scan reads only files Claude Code already writes while changing nothing in anyone&amp;rsquo;s configuration.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Running both against four live sessions&#10; &lt;div id="running-both-against-four-live-sessions" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#running-both-against-four-live-sessions" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Agreement between two independent routes is the only verification available when there is no ground truth to check against, so I ran them side by side. Three sessions agreed exactly: 34/34, 43/43, 16/16. The fourth converged once its turn completed.&lt;/p&gt;&#10;&lt;p&gt;Getting there took two fixes that only live data surfaced.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;One pane out of four had no session id.&lt;/strong&gt; herdr resolved three of four Claude panes and the fourth came back with &lt;code&gt;agent_session: null&lt;/code&gt;. The fix needed no configuration: &lt;code&gt;claude agents --json&lt;/code&gt; maps pid to session id, and herdr already knows each pane&amp;rsquo;s processes, so joining the two closes the gap.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;That same pane was rate-limited, and its transcript lied.&lt;/strong&gt; A rate-limited session&amp;rsquo;s transcript ends in &lt;code&gt;&amp;lt;synthetic&amp;gt;&lt;/code&gt; records flagged &lt;code&gt;isApiErrorMessage&lt;/code&gt;, carrying all-zero usage. Six of them in a row, in this case.&lt;/p&gt;&#10;&lt;p&gt;Read the newest usage record blindly and you get &lt;strong&gt;0% for a session actually sitting at 34%&lt;/strong&gt;. Not a small error, and not a random one. It always reports empty, and it reports empty exactly when the session is in trouble, which is when you are most likely to be looking at the key. &amp;ldquo;Use the newest record&amp;rdquo; is a reasonable-sounding heuristic that happens to select for the records carrying no information.&lt;/p&gt;&#10;&lt;p&gt;The fix is to skip records flagged as API errors while scanning backwards for the last real usage figure. Obvious once you have seen it, invisible until a session gets rate-limited while you happen to be watching.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Lessons&#10; &lt;div id="lessons" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lessons" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;A percentage is a division, and the denominator deserves as much scrutiny as the numerator. Report the number you measured; you are only asserting the one you derived.&lt;/li&gt;&#10;&lt;li&gt;Prefer one named constant to a lookup table when neither is discoverable. A table rots per release and keeps answering; a constant is one thing to be wrong about and one thing to fix.&lt;/li&gt;&#10;&lt;li&gt;Clamp derived percentages at both ends. A 216% ring is your assumption failing in public, and the clamp converts it into the least-wrong reading available.&lt;/li&gt;&#10;&lt;li&gt;A machine-readable API answer is only useful if your key is unambiguous. Per-model context windows do not help when the field you hold has already dropped the variant marker.&lt;/li&gt;&#10;&lt;li&gt;When two independent routes to a number exist, run both against live data and compare. Exact agreement on three sessions surfaced two bugs neither route would have revealed alone.&lt;/li&gt;&#10;&lt;li&gt;Read the flags on a record before trusting its contents. Synthetic error records with zero usage will cheerfully answer &amp;ldquo;0%&amp;rdquo; for a session at 34%, and they show up precisely when things are going wrong.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;References&#10; &lt;div id="references" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#references" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Claude Code statusline docs, the payload carrying &lt;code&gt;context_window.context_window_size&lt;/code&gt;: &lt;a href="https://code.claude.com/docs/en/statusline" target="_blank" rel="noreferrer"&gt;https://code.claude.com/docs/en/statusline&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Claude Code hooks reference, on what hook payloads carry: &lt;a href="https://code.claude.com/docs/en/hooks" target="_blank" rel="noreferrer"&gt;https://code.claude.com/docs/en/hooks&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Anthropic models overview, per-model context windows: &lt;a href="https://platform.claude.com/docs/en/about-claude/models/overview" target="_blank" rel="noreferrer"&gt;https://platform.claude.com/docs/en/about-claude/models/overview&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The statusline side channel this builds on: &lt;a href="https://nick-liu.com/posts/statusline-side-channel/" &gt;The Claude Code statusline is a per-turn telemetry side channel&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The ring this feeds: &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" &gt;Every context ring read 10%. Two bugs, and fixing one hid the other.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;herddeck-ctx-scan&lt;/code&gt;, the transcript scanner: &lt;a href="https://github.com/nickboy/herddeck/blob/main/scripts/herddeck-ctx-scan" target="_blank" rel="noreferrer"&gt;https://github.com/nickboy/herddeck/blob/main/scripts/herddeck-ctx-scan&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>launchctl bootout returned 0. The label was still there. Bootstrap failed 5.</title><link>https://nick-liu.com/posts/launchd-bootout-race/</link><pubDate>Sat, 05 Sep 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/launchd-bootout-race/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;`launchctl bootout` exited 0. `launchctl bootstrap`, on the very next line, failed with `Bootstrap failed: 5: Input/output error` because the label was still loaded. Then the bootout finished on its own schedule and the label went away, leaving a machine with no daemon at all and an installer that had just told me it was done. This one is mine: I caused it while fixing something else.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;launchd&lt;/strong&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;I have &lt;a href="https://nick-liu.com/posts/launchd-bootstrap-debugging/" &gt;written before&lt;/a&gt; about &lt;code&gt;launchctl unload&lt;/code&gt; returning 0 while the daemon kept running, which turned out to be KeepAlive&amp;rsquo;s supervisor racing the teardown. This is the sibling. Same API, opposite failure, and where that one was launchd surprising me, this one I built myself.&lt;/p&gt;</description><content:encoded>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;`launchctl bootout` exited 0. `launchctl bootstrap`, on the very next line, failed with `Bootstrap failed: 5: Input/output error` because the label was still loaded. Then the bootout finished on its own schedule and the label went away, leaving a machine with no daemon at all and an installer that had just told me it was done. This one is mine: I caused it while fixing something else.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;launchd&lt;/strong&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;I have &lt;a href="https://nick-liu.com/posts/launchd-bootstrap-debugging/" &gt;written before&lt;/a&gt; about &lt;code&gt;launchctl unload&lt;/code&gt; returning 0 while the daemon kept running, which turned out to be KeepAlive&amp;rsquo;s supervisor racing the teardown. This is the sibling. Same API, opposite failure, and where that one was launchd surprising me, this one I built myself.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The idempotency fix that caused it&#10; &lt;div id="the-idempotency-fix-that-caused-it" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-idempotency-fix-that-caused-it" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;herddeck install&lt;/code&gt; should be re-runnable. You edit a config, you run install again, you expect it to work. That is table stakes for an installer.&lt;/p&gt;&#10;&lt;p&gt;launchd disagrees. Bootstrapping a label that is already loaded is an error, so a second &lt;code&gt;install&lt;/code&gt; would print something like this and stop:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Bootstrap failed: 5: Input/output error&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Errno 5 is &lt;code&gt;EIO&lt;/code&gt;, which launchd reuses as a catch-all for &amp;ldquo;I refuse&amp;rdquo;. In this case it means &amp;ldquo;already loaded&amp;rdquo;, which is a perfectly harmless condition: the daemon you wanted running is running.&lt;/p&gt;&#10;&lt;p&gt;So I did the obvious thing. Boot it out first, then bootstrap. Now install is idempotent.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;What &lt;code&gt;bootout&lt;/code&gt; actually returns&#10; &lt;div id="what-bootout-actually-returns" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-bootout-actually-returns" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;&lt;code&gt;launchctl bootout&lt;/code&gt; returns as soon as launchd has accepted the request. It does not wait for the job to be torn down, the processes to exit, or the label to leave launchd&amp;rsquo;s tables.&lt;/p&gt;&#10;&lt;p&gt;The sequence I created:&lt;/p&gt;&#10;&lt;div class="not-prose diagram diagram-light"&gt;&lt;svg id="d48bc3a78de8a55aflight" width="100%" xmlns="http://www.w3.org/2000/svg" style="max-width: 594.5px;" viewBox="-50 -10 594.5 451" role="graphics-document document" aria-roledescription="sequence"&gt;&lt;g&gt;&lt;rect x="341" y="365" fill="#eaeaea" stroke="#666" width="150" height="65" name="L" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="416" y="397.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="416" dy="0"&gt;launchd&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x="0" y="365" fill="#eaeaea" stroke="#666" width="150" height="65" name="I" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="75" y="397.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;herddeck install&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor11" x1="416" y1="65" x2="416" y2="365" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="L" data-et="life-line" data-id="L"&gt;&lt;/line&gt;&lt;g id="root-11" data-et="participant" data-type="participant" data-id="L"&gt;&lt;rect x="341" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="L" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="416" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="416" dy="0"&gt;launchd&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor10" x1="75" y1="65" x2="75" y2="365" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="I" data-et="life-line" data-id="I"&gt;&lt;/line&gt;&lt;g id="root-10" data-et="participant" data-type="participant" data-id="I"&gt;&lt;rect x="0" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="I" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="75" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;herddeck install&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;style&gt;#d48bc3a78de8a55aflight{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#d48bc3a78de8a55aflight .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#d48bc3a78de8a55aflight .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#d48bc3a78de8a55aflight .error-icon{fill:rgb(255, 255, 255);}#d48bc3a78de8a55aflight .error-text{fill:#000000;stroke:#000000;}#d48bc3a78de8a55aflight .edge-thickness-normal{stroke-width:1px;}#d48bc3a78de8a55aflight .edge-thickness-thick{stroke-width:3.5px;}#d48bc3a78de8a55aflight .edge-pattern-solid{stroke-dasharray:0;}#d48bc3a78de8a55aflight .edge-thickness-invisible{stroke-width:0;fill:none;}#d48bc3a78de8a55aflight .edge-pattern-dashed{stroke-dasharray:3;}#d48bc3a78de8a55aflight .edge-pattern-dotted{stroke-dasharray:2;}#d48bc3a78de8a55aflight .marker{fill:rgb(74, 86, 92);stroke:rgb(74, 86, 92);}#d48bc3a78de8a55aflight .marker.cross{stroke:rgb(74, 86, 92);}#d48bc3a78de8a55aflight svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#d48bc3a78de8a55aflight p{margin:0;}#d48bc3a78de8a55aflight .actor{stroke:rgb(204, 216, 222);fill:rgb(255, 255, 255);stroke-width:1;}#d48bc3a78de8a55aflight rect.actor.outer-path[data-look="neo"]{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55aflight rect.note[data-look="neo"]{stroke:hsl(52.6829268293, 60%, 73.9215686275%);fill:#fff5ad;filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55aflight text.actor&amp;gt;tspan{fill:#333;stroke:none;}#d48bc3a78de8a55aflight .actor-line{stroke:rgb(204, 216, 222);}#d48bc3a78de8a55aflight .innerArc{stroke-width:1.5;stroke-dasharray:none;}#d48bc3a78de8a55aflight .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#d48bc3a78de8a55aflight .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#d48bc3a78de8a55aflight [id$="-arrowhead"] path{fill:#333;stroke:#333;}#d48bc3a78de8a55aflight .sequenceNumber{fill:#b5a9a3;}#d48bc3a78de8a55aflight [id$="-sequencenumber"]{fill:#333;}#d48bc3a78de8a55aflight [id$="-crosshead"] path{fill:#333;stroke:#333;}#d48bc3a78de8a55aflight .messageText{fill:#333;stroke:none;}#d48bc3a78de8a55aflight .labelBox{stroke:rgb(204, 216, 222);fill:rgb(255, 255, 255);filter:none;}#d48bc3a78de8a55aflight .labelText,#d48bc3a78de8a55aflight .labelText&amp;gt;tspan{fill:#333;stroke:none;}#d48bc3a78de8a55aflight .loopText,#d48bc3a78de8a55aflight .loopText&amp;gt;tspan{fill:#333;stroke:none;}#d48bc3a78de8a55aflight .sectionTitle,#d48bc3a78de8a55aflight .sectionTitle&amp;gt;tspan{fill:#333;stroke:none;}#d48bc3a78de8a55aflight .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:rgb(204, 216, 222);fill:rgb(204, 216, 222);}#d48bc3a78de8a55aflight .note{stroke:hsl(52.6829268293, 60%, 73.9215686275%);fill:#fff5ad;}#d48bc3a78de8a55aflight .noteText,#d48bc3a78de8a55aflight .noteText&amp;gt;tspan{fill:#333;stroke:none;font-weight:normal;}#d48bc3a78de8a55aflight .activation0{fill:rgb(182, 240, 255);stroke:hsl(192.3287671233, 100%, 75.6862745098%);}#d48bc3a78de8a55aflight .activation1{fill:rgb(182, 240, 255);stroke:hsl(192.3287671233, 100%, 75.6862745098%);}#d48bc3a78de8a55aflight .activation2{fill:rgb(182, 240, 255);stroke:hsl(192.3287671233, 100%, 75.6862745098%);}#d48bc3a78de8a55aflight .actorPopupMenu{position:absolute;}#d48bc3a78de8a55aflight .actorPopupMenuPanel{position:absolute;fill:rgb(255, 255, 255);box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#d48bc3a78de8a55aflight .actor-man circle,#d48bc3a78de8a55aflight line{fill:rgb(255, 255, 255);stroke-width:2px;}#d48bc3a78de8a55aflight g rect.rect{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));stroke:rgb(204, 216, 222);}#d48bc3a78de8a55aflight .node .neo-node{stroke:rgb(204, 216, 222);}#d48bc3a78de8a55aflight [data-look="neo"].node rect,#d48bc3a78de8a55aflight [data-look="neo"].cluster rect,#d48bc3a78de8a55aflight [data-look="neo"].node polygon{stroke:url(#d48bc3a78de8a55aflight-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55aflight [data-look="neo"].swimlane.cluster rect{filter:none;}#d48bc3a78de8a55aflight [data-look="neo"].node path{stroke:url(#d48bc3a78de8a55aflight-gradient);stroke-width:1px;}#d48bc3a78de8a55aflight [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55aflight [data-look="neo"].node .neo-line path{stroke:rgb(204, 216, 222);filter:none;}#d48bc3a78de8a55aflight [data-look="neo"].node circle{stroke:url(#d48bc3a78de8a55aflight-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55aflight [data-look="neo"].node circle .state-start{fill:#000000;}#d48bc3a78de8a55aflight [data-look="neo"].icon-shape .icon{fill:url(#d48bc3a78de8a55aflight-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55aflight [data-look="neo"].icon-shape .icon-neo path{stroke:url(#d48bc3a78de8a55aflight-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55aflight :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;/g&gt;&lt;defs&gt;&lt;symbol id="d48bc3a78de8a55aflight-computer" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="d48bc3a78de8a55aflight-database" fill-rule="evenodd" clip-rule="evenodd"&gt;&lt;path transform="scale(.5)" d="M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="d48bc3a78de8a55aflight-clock" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55aflight-arrowhead" refX="7.9" refY="5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M -1 0 L 10 5 L 0 10 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55aflight-crosshead" markerWidth="15" markerHeight="8" orient="auto" refX="4" refY="4.5"&gt;&lt;path fill="none" stroke="#000000" stroke-width="1pt" d="M 1,2 L 6,7 M 6,2 L 1,7" style="stroke-dasharray: 0, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55aflight-filled-head" refX="15.5" refY="7" markerWidth="20" markerHeight="28" orient="auto"&gt;&lt;path d="M 18,7 L9,13 L14,7 L9,1 Z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55aflight-sequencenumber" refX="15" refY="15" markerWidth="60" markerHeight="40" orient="auto"&gt;&lt;circle cx="15" cy="15" r="6"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55aflight-solidTopArrowHead" refX="7.9" refY="7.25" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 8 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55aflight-solidBottomArrowHead" refX="7.9" refY="0.75" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 0 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55aflight-stickTopArrowHead" refX="7.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 7 7" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55aflight-stickBottomArrowHead" refX="7.5" refY="0" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 7 L 7 0" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;g data-et="note" data-id="i4"&gt;&lt;rect x="337.5" y="259" fill="#EDF2AE" stroke="#666" width="157" height="38" class="note"&gt;&lt;/rect&gt;&lt;text x="416" y="264" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="416"&gt;bootout completes&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g data-et="note" data-id="i5"&gt;&lt;rect x="50" y="307" fill="#EDF2AE" stroke="#666" width="391" height="38" class="note"&gt;&lt;/rect&gt;&lt;text x="246" y="312" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="246"&gt;nothing is loaded&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;text x="244" y="80" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;bootout gui/501/…herddeck.daemon&lt;/text&gt;&lt;line x1="76" y1="111" x2="412" y2="111" class="messageLine0" data-et="message" data-id="i0" data-from="I" data-to="L" stroke-width="2" stroke="none" marker-end="url(#d48bc3a78de8a55aflight-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="247" y="126" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;exit 0 (accepted, not finished)&lt;/text&gt;&lt;line x1="415" y1="157" x2="79" y2="157" class="messageLine1" data-et="message" data-id="i1" data-from="L" data-to="I" stroke-width="2" stroke="none" marker-end="url(#d48bc3a78de8a55aflight-arrowhead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;text x="244" y="172" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;bootstrap gui/501 …plist&lt;/text&gt;&lt;line x1="76" y1="203" x2="412" y2="203" class="messageLine0" data-et="message" data-id="i2" data-from="I" data-to="L" stroke-width="2" stroke="none" marker-end="url(#d48bc3a78de8a55aflight-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="247" y="218" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;Bootstrap failed: 5 (label still present)&lt;/text&gt;&lt;line x1="415" y1="249" x2="79" y2="249" class="messageLine1" data-et="message" data-id="i3" data-from="L" data-to="I" stroke-width="2" stroke="none" marker-end="url(#d48bc3a78de8a55aflight-arrowhead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/div&gt;&#10; &lt;div class="not-prose diagram diagram-dark"&gt;&lt;svg id="d48bc3a78de8a55afdark" width="100%" xmlns="http://www.w3.org/2000/svg" style="max-width: 594.5px;" viewBox="-50 -10 594.5 451" role="graphics-document document" aria-roledescription="sequence"&gt;&lt;g&gt;&lt;rect x="341" y="365" fill="#eaeaea" stroke="#666" width="150" height="65" name="L" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="416" y="397.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="416" dy="0"&gt;launchd&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x="0" y="365" fill="#eaeaea" stroke="#666" width="150" height="65" name="I" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="75" y="397.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;herddeck install&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor13" x1="416" y1="65" x2="416" y2="365" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="L" data-et="life-line" data-id="L"&gt;&lt;/line&gt;&lt;g id="root-13" data-et="participant" data-type="participant" data-id="L"&gt;&lt;rect x="341" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="L" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="416" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="416" dy="0"&gt;launchd&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor12" x1="75" y1="65" x2="75" y2="365" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="I" data-et="life-line" data-id="I"&gt;&lt;/line&gt;&lt;g id="root-12" data-et="participant" data-type="participant" data-id="I"&gt;&lt;rect x="0" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="I" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="75" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;herddeck install&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;style&gt;#d48bc3a78de8a55afdark{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#ccc;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#d48bc3a78de8a55afdark .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#d48bc3a78de8a55afdark .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#d48bc3a78de8a55afdark .error-icon{fill:#a44141;}#d48bc3a78de8a55afdark .error-text{fill:#ddd;stroke:#ddd;}#d48bc3a78de8a55afdark .edge-thickness-normal{stroke-width:1px;}#d48bc3a78de8a55afdark .edge-thickness-thick{stroke-width:3.5px;}#d48bc3a78de8a55afdark .edge-pattern-solid{stroke-dasharray:0;}#d48bc3a78de8a55afdark .edge-thickness-invisible{stroke-width:0;fill:none;}#d48bc3a78de8a55afdark .edge-pattern-dashed{stroke-dasharray:3;}#d48bc3a78de8a55afdark .edge-pattern-dotted{stroke-dasharray:2;}#d48bc3a78de8a55afdark .marker{fill:lightgrey;stroke:lightgrey;}#d48bc3a78de8a55afdark .marker.cross{stroke:lightgrey;}#d48bc3a78de8a55afdark svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#d48bc3a78de8a55afdark p{margin:0;}#d48bc3a78de8a55afdark .actor{stroke:#ccc;fill:#1f2020;stroke-width:1;}#d48bc3a78de8a55afdark rect.actor.outer-path[data-look="neo"]{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55afdark rect.note[data-look="neo"]{stroke:hsl(180, 0%, 18.3529411765%);fill:hsl(180, 1.5873015873%, 28.3529411765%);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55afdark text.actor&amp;gt;tspan{fill:lightgrey;stroke:none;}#d48bc3a78de8a55afdark .actor-line{stroke:#ccc;}#d48bc3a78de8a55afdark .innerArc{stroke-width:1.5;stroke-dasharray:none;}#d48bc3a78de8a55afdark .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:lightgrey;}#d48bc3a78de8a55afdark .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:lightgrey;}#d48bc3a78de8a55afdark [id$="-arrowhead"] path{fill:lightgrey;stroke:lightgrey;}#d48bc3a78de8a55afdark .sequenceNumber{fill:black;}#d48bc3a78de8a55afdark [id$="-sequencenumber"]{fill:lightgrey;}#d48bc3a78de8a55afdark [id$="-crosshead"] path{fill:lightgrey;stroke:lightgrey;}#d48bc3a78de8a55afdark .messageText{fill:lightgrey;stroke:none;}#d48bc3a78de8a55afdark .labelBox{stroke:#ccc;fill:#1f2020;filter:none;}#d48bc3a78de8a55afdark .labelText,#d48bc3a78de8a55afdark .labelText&amp;gt;tspan{fill:lightgrey;stroke:none;}#d48bc3a78de8a55afdark .loopText,#d48bc3a78de8a55afdark .loopText&amp;gt;tspan{fill:lightgrey;stroke:none;}#d48bc3a78de8a55afdark .sectionTitle,#d48bc3a78de8a55afdark .sectionTitle&amp;gt;tspan{fill:lightgrey;stroke:none;}#d48bc3a78de8a55afdark .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:#ccc;fill:#ccc;}#d48bc3a78de8a55afdark .note{stroke:hsl(180, 0%, 18.3529411765%);fill:hsl(180, 1.5873015873%, 28.3529411765%);}#d48bc3a78de8a55afdark .noteText,#d48bc3a78de8a55afdark .noteText&amp;gt;tspan{fill:rgb(183.8476190475, 181.5523809523, 181.5523809523);stroke:none;font-weight:normal;}#d48bc3a78de8a55afdark .activation0{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:#ccc;}#d48bc3a78de8a55afdark .activation1{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:#ccc;}#d48bc3a78de8a55afdark .activation2{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:#ccc;}#d48bc3a78de8a55afdark .actorPopupMenu{position:absolute;}#d48bc3a78de8a55afdark .actorPopupMenuPanel{position:absolute;fill:#1f2020;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#d48bc3a78de8a55afdark .actor-man circle,#d48bc3a78de8a55afdark line{fill:#1f2020;stroke-width:2px;}#d48bc3a78de8a55afdark g rect.rect{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));stroke:#ccc;}#d48bc3a78de8a55afdark .node .neo-node{stroke:#ccc;}#d48bc3a78de8a55afdark [data-look="neo"].node rect,#d48bc3a78de8a55afdark [data-look="neo"].cluster rect,#d48bc3a78de8a55afdark [data-look="neo"].node polygon{stroke:url(#d48bc3a78de8a55afdark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55afdark [data-look="neo"].swimlane.cluster rect{filter:none;}#d48bc3a78de8a55afdark [data-look="neo"].node path{stroke:url(#d48bc3a78de8a55afdark-gradient);stroke-width:1px;}#d48bc3a78de8a55afdark [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55afdark [data-look="neo"].node .neo-line path{stroke:#ccc;filter:none;}#d48bc3a78de8a55afdark [data-look="neo"].node circle{stroke:url(#d48bc3a78de8a55afdark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55afdark [data-look="neo"].node circle .state-start{fill:#000000;}#d48bc3a78de8a55afdark [data-look="neo"].icon-shape .icon{fill:url(#d48bc3a78de8a55afdark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55afdark [data-look="neo"].icon-shape .icon-neo path{stroke:url(#d48bc3a78de8a55afdark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d48bc3a78de8a55afdark :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;/g&gt;&lt;defs&gt;&lt;symbol id="d48bc3a78de8a55afdark-computer" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="d48bc3a78de8a55afdark-database" fill-rule="evenodd" clip-rule="evenodd"&gt;&lt;path transform="scale(.5)" d="M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="d48bc3a78de8a55afdark-clock" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55afdark-arrowhead" refX="7.9" refY="5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M -1 0 L 10 5 L 0 10 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55afdark-crosshead" markerWidth="15" markerHeight="8" orient="auto" refX="4" refY="4.5"&gt;&lt;path fill="none" stroke="#000000" stroke-width="1pt" d="M 1,2 L 6,7 M 6,2 L 1,7" style="stroke-dasharray: 0, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55afdark-filled-head" refX="15.5" refY="7" markerWidth="20" markerHeight="28" orient="auto"&gt;&lt;path d="M 18,7 L9,13 L14,7 L9,1 Z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55afdark-sequencenumber" refX="15" refY="15" markerWidth="60" markerHeight="40" orient="auto"&gt;&lt;circle cx="15" cy="15" r="6"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55afdark-solidTopArrowHead" refX="7.9" refY="7.25" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 8 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55afdark-solidBottomArrowHead" refX="7.9" refY="0.75" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 0 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55afdark-stickTopArrowHead" refX="7.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 7 7" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="d48bc3a78de8a55afdark-stickBottomArrowHead" refX="7.5" refY="0" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 7 L 7 0" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;g data-et="note" data-id="i4"&gt;&lt;rect x="337.5" y="259" fill="#EDF2AE" stroke="#666" width="157" height="38" class="note"&gt;&lt;/rect&gt;&lt;text x="416" y="264" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="416"&gt;bootout completes&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g data-et="note" data-id="i5"&gt;&lt;rect x="50" y="307" fill="#EDF2AE" stroke="#666" width="391" height="38" class="note"&gt;&lt;/rect&gt;&lt;text x="246" y="312" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="246"&gt;nothing is loaded&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;text x="244" y="80" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;bootout gui/501/…herddeck.daemon&lt;/text&gt;&lt;line x1="76" y1="111" x2="412" y2="111" class="messageLine0" data-et="message" data-id="i0" data-from="I" data-to="L" stroke-width="2" stroke="none" marker-end="url(#d48bc3a78de8a55afdark-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="247" y="126" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;exit 0 (accepted, not finished)&lt;/text&gt;&lt;line x1="415" y1="157" x2="79" y2="157" class="messageLine1" data-et="message" data-id="i1" data-from="L" data-to="I" stroke-width="2" stroke="none" marker-end="url(#d48bc3a78de8a55afdark-arrowhead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;text x="244" y="172" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;bootstrap gui/501 …plist&lt;/text&gt;&lt;line x1="76" y1="203" x2="412" y2="203" class="messageLine0" data-et="message" data-id="i2" data-from="I" data-to="L" stroke-width="2" stroke="none" marker-end="url(#d48bc3a78de8a55afdark-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="247" y="218" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;Bootstrap failed: 5 (label still present)&lt;/text&gt;&lt;line x1="415" y1="249" x2="79" y2="249" class="messageLine1" data-et="message" data-id="i3" data-from="L" data-to="I" stroke-width="2" stroke="none" marker-end="url(#d48bc3a78de8a55afdark-arrowhead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/div&gt;&#10;&lt;p&gt;The bootstrap fails against a label that is on its way out. Then the bootout completes. The net result is that nothing is loaded, which is the one outcome neither command asked for.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Why the failure is silent&#10; &lt;div id="why-the-failure-is-silent" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#why-the-failure-is-silent" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;The old behaviour was a loud error in a harmless situation. The new behaviour is a quiet error in a broken one, and the asymmetry is the whole lesson.&lt;/p&gt;&#10;&lt;p&gt;If you run install twice in a row, the second run prints its bootstrap failure and you have a running daemon anyway, because the bootout raced and lost. If you run it once after an upgrade, you may get a bootout that wins, a bootstrap that fails, and no daemon. The daemon does not crash. It was never started. There is no crash log, no &lt;code&gt;KeepAlive&lt;/code&gt; respawn, and nothing in &lt;code&gt;launchctl list&lt;/code&gt; to look at, because the label is genuinely gone.&lt;/p&gt;&#10;&lt;p&gt;The symptom is that HerdDeck simply stops existing until you run install a second time, which of course works, because now there is nothing to boot out. A bug that is fixed by doing the thing again is a bug that hides for a long time.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The fix: poll, then retry, both bounded&#10; &lt;div id="the-fix-poll-then-retry-both-bounded" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-fix-poll-then-retry-both-bounded" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;The correct move is to stop trusting the return code and ask launchd directly whether the label is still there.&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/** Poll interval and cap while waiting for `launchctl bootout` to&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * actually finish, plus how many times to retry a bootstrap that fails&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * anyway. 20 x 100ms is far beyond the observed settle time and still&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * bounded at 2s. */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UNLOAD_WAIT_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;UNLOAD_WAIT_ATTEMPTS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BOOTSTRAP_RETRIES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;&lt;code&gt;launchctl print gui/&amp;lt;uid&amp;gt;/&amp;lt;label&amp;gt;&lt;/code&gt; exits non-zero once the label is really gone, which makes it a usable predicate:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLoaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;async&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;launchctl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;print&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`gui/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;LAUNCHD_LABEL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;])).&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;alreadyLoaded&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;isLoaded&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;alreadyLoaded&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;launchctl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bootout&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`gui/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;LAUNCHD_LABEL&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;UNLOAD_WAIT_ATTEMPTS&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;isLoaded&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UNLOAD_WAIT_MS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;launchctl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bootstrap&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`gui/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;plistPath&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;BOOTSTRAP_RETRIES&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exitCode&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UNLOAD_WAIT_MS&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;launchctl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;bootstrap&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sb"&gt;`gui/&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uid&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;plistPath&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Both loops are bounded, and that matters more than the polling. The wait caps at two seconds, far past anything I measured. The bootstrap retries twice and then surfaces its error like it always did. A genuinely broken launchd, a malformed plist, a permissions problem, all still fail loudly. What the bounds buy is that a slow unload no longer looks like a broken install, and a broken install still looks broken.&lt;/p&gt;&#10;&lt;p&gt;The test that pins it is named for the outcome rather than the mechanism: &amp;ldquo;waits out an async bootout instead of leaving nothing loaded&amp;rdquo;. If someone later decides the polling loop is superstition, the test name tells them what breaks.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The pattern, and its sibling&#10; &lt;div id="the-pattern-and-its-sibling" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-pattern-and-its-sibling" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Put the two launchd posts next to each other and they are the same shape from opposite sides.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Earlier post&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;This post&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Command&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;launchctl unload&lt;/code&gt; / &lt;code&gt;bootout&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;launchctl bootout&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Returned&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Reality&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;job unloaded, process still running&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;request accepted, label still loaded&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Consequence&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;orphaned daemon holding a port&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;nothing running at all&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Cause&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;KeepAlive respawn racing the teardown&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;teardown not finished when the call returns&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;In both cases the exit code is honest about what the command did and silent about what launchd had not finished doing yet. &lt;code&gt;launchctl&lt;/code&gt; reports on the request, not on the world.&lt;/p&gt;&#10;&lt;p&gt;And in both cases the fix is the same in outline: after asking launchd to change something, verify the change independently, with a bound. &lt;code&gt;pgrep&lt;/code&gt; in one direction, &lt;code&gt;launchctl print&lt;/code&gt; in the other.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Lessons&#10; &lt;div id="lessons" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lessons" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;launchctl bootout&lt;/code&gt; returns when launchd accepts the request, not when the job is gone. The exit code describes the request, not the world.&lt;/li&gt;&#10;&lt;li&gt;Adding a safety can convert a loud harmless failure into a silent harmful one. &amp;ldquo;Bootstrap refused because it is already loaded&amp;rdquo; was noisy and fine. &amp;ldquo;Nothing is loaded&amp;rdquo; is quiet and broken.&lt;/li&gt;&#10;&lt;li&gt;Suspect any bug that goes away when you run the command a second time. That shape is a race, and running it twice is not a workaround, it is the reproduction.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;launchctl print gui/&amp;lt;uid&amp;gt;/&amp;lt;label&amp;gt;&lt;/code&gt; is the predicate you want. Its exit code answers &amp;ldquo;is this label loaded right now&amp;rdquo;, which is the question &lt;code&gt;bootout&lt;/code&gt; will not answer for you.&lt;/li&gt;&#10;&lt;li&gt;Bound the poll and bound the retry. Unbounded waiting turns a broken plist into a hang, and the whole value of the fix is that real failures still fail.&lt;/li&gt;&#10;&lt;li&gt;Name the test after the outcome, not the mechanism. &amp;ldquo;Waits out an async bootout instead of leaving nothing loaded&amp;rdquo; survives a refactor that deletes the loop.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;References&#10; &lt;div id="references" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#references" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;launchctl(1)&lt;/code&gt; and &lt;code&gt;launchd.plist(5)&lt;/code&gt; man pages. Accurate about verbs, silent about lifecycle ordering, which is the part that bites&lt;/li&gt;&#10;&lt;li&gt;The sibling failure on the same API: &lt;a href="https://nick-liu.com/posts/launchd-bootstrap-debugging/" &gt;launchctl unload returned 0. The daemon was still running. KeepAlive raced.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The project this installer belongs to: &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" &gt;I deleted six subsystems by swapping one dependency.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;HerdDeck installer, the bootout wait and bootstrap retry: &lt;a href="https://github.com/nickboy/herddeck/blob/main/packages/cli/src/herddeck.ts" target="_blank" rel="noreferrer"&gt;&lt;code&gt;packages/cli/src/herddeck.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>The tunnel was up. The socket existed. Nothing was on the other end.</title><link>https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/</link><pubDate>Thu, 03 Sep 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;The daemon reported the tunnel as established. The local socket file was right there on disk with the right permissions. The target sat at `offline` forever. Meanwhile `connect to /home/nick/.config/herdr/sessions/main/herdr.sock port 0 failed` was being written to a stderr stream that nothing in my process ever read, several times a second, for as long as I left it running.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 5 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me."&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question."&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other."&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%."&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end." aria-current="step"&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;herdr&lt;/strong&gt; &lt;code&gt;0.8.0&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;HerdDeck drives agents on a second machine. My laptop has the Stream Deck and the daemon; the desktop runs herdr and the agents actually doing work. They are joined by &lt;code&gt;ssh -N -L &amp;lt;local.sock&amp;gt;:&amp;lt;remote.sock&amp;gt; host&lt;/code&gt;, a Unix-domain socket forward, and the daemon speaks the same protocol to a forwarded socket as it does to a local one.&lt;/p&gt;</description><content:encoded>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;The daemon reported the tunnel as established. The local socket file was right there on disk with the right permissions. The target sat at `offline` forever. Meanwhile `connect to /home/nick/.config/herdr/sessions/main/herdr.sock port 0 failed` was being written to a stderr stream that nothing in my process ever read, several times a second, for as long as I left it running.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 5 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me."&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question."&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other."&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%."&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end." aria-current="step"&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;herdr&lt;/strong&gt; &lt;code&gt;0.8.0&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;HerdDeck drives agents on a second machine. My laptop has the Stream Deck and the daemon; the desktop runs herdr and the agents actually doing work. They are joined by &lt;code&gt;ssh -N -L &amp;lt;local.sock&amp;gt;:&amp;lt;remote.sock&amp;gt; host&lt;/code&gt;, a Unix-domain socket forward, and the daemon speaks the same protocol to a forwarded socket as it does to a local one.&lt;/p&gt;&#10;&lt;p&gt;That symmetry is the entire reason remote support was cheap. It is also what made this bug possible, because &amp;ldquo;I have a socket&amp;rdquo; is a much weaker statement than it looks.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;&lt;code&gt;ssh -L&lt;/code&gt; binds before it connects&#10; &lt;div id="ssh--l-binds-before-it-connects" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#ssh--l-binds-before-it-connects" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;A local forward binds the listening end immediately. It does not, and cannot, verify that anything is listening on the far side, because nothing tries to reach the far side until you open a connection through it.&lt;/p&gt;&#10;&lt;p&gt;So the moment &lt;code&gt;ssh -N -L&lt;/code&gt; starts, you have a socket file. It is a real socket. You can &lt;code&gt;connect()&lt;/code&gt; to it. And when you do, ssh opens a channel, asks the remote sshd to reach the target path, the remote sshd fails, and ssh writes a message about it to &lt;em&gt;its&lt;/em&gt; stderr and closes your channel.&lt;/p&gt;&#10;&lt;p&gt;My daemon&amp;rsquo;s definition of &amp;ldquo;established&amp;rdquo; was the existence of that file.&lt;/p&gt;&#10;&lt;p&gt;Every failure mode on the far side therefore looked identical and looked fine. Remote herdr not running: tunnel up, target offline. Wrong absolute path in the config: tunnel up, target offline. Session socket not created because that named session had never been started on the remote: tunnel up, target offline.&lt;/p&gt;&#10;&lt;p&gt;Three different problems, one symptom, and the symptom actively pointed away from the cause, because the tunnel was the part reporting healthy.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The error was there the whole time&#10; &lt;div id="the-error-was-there-the-whole-time" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-error-was-there-the-whole-time" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;The failures are asynchronous. ssh does not fail at spawn; it fails per channel, later, on stderr. I was reading stderr during spawn to catch startup errors, then dropping the stream on the floor once the process was up.&lt;/p&gt;&#10;&lt;p&gt;Everything the debugger needed was in the part I stopped reading.&lt;/p&gt;&#10;&lt;p&gt;Two changes. First, stderr is drained for the tunnel&amp;rsquo;s entire lifetime into a small per-target ring buffer, capped, so a chatty failure cannot grow without bound and the last few dozen lines are always available for diagnostics. Second, and more important, establishing a tunnel now ends by proving it:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;bind local socket -&amp;gt; ping-probe THROUGH the socket -&amp;gt; declare established&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;The probe is a real protocol &lt;code&gt;ping&lt;/code&gt; through the forwarded socket with a 4-second timeout. If it fails, the tunnel is not established, regardless of what the filesystem says, and the failure joins the retry path with the stderr tail attached to the error.&lt;/p&gt;&#10;&lt;p&gt;The observable difference is the whole point. Before: tunnel green, target offline indefinitely, zero clues. After: it fails within seconds with &lt;code&gt;channel 1: open failed: connect failed&lt;/code&gt; and the full &lt;code&gt;-L&lt;/code&gt; path mapping in the log, which names the wrong path directly.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;ControlMaster pollution, in both directions&#10; &lt;div id="controlmaster-pollution-in-both-directions" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#controlmaster-pollution-in-both-directions" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;This one I would never have found by reading code, because the bug lived in the interaction between correct code and my personal SSH config.&lt;/p&gt;&#10;&lt;p&gt;Most developers have &lt;code&gt;ControlMaster auto&lt;/code&gt; set. It multiplexes SSH sessions to the same host over one connection, which is why your second &lt;code&gt;ssh&lt;/code&gt; to a box you are already on connects instantly. It is a good default and I have had it on for years.&lt;/p&gt;&#10;&lt;p&gt;A long-lived tunnel joining that multiplexing arrangement fails in two directions, and the second is nastier.&lt;/p&gt;&#10;&lt;div class="not-prose diagram diagram-light"&gt;&lt;svg id="da14699abd285f1b6light" width="100%" xmlns="http://www.w3.org/2000/svg" style="max-width: 878.5px;" viewBox="-50 -10 878.5 421" role="graphics-document document" aria-roledescription="sequence"&gt;&lt;g&gt;&lt;rect x="589" y="335" fill="#eaeaea" stroke="#666" width="150" height="65" name="T" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="664" y="367.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="664" dy="0"&gt;herddeck tunnel&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x="272" y="335" fill="#eaeaea" stroke="#666" width="150" height="65" name="M" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="347" y="367.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="347" dy="0"&gt;ControlMaster&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x="0" y="335" fill="#eaeaea" stroke="#666" width="150" height="65" name="S" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="75" y="367.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;your shell&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor2" x1="664" y1="65" x2="664" y2="335" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="T" data-et="life-line" data-id="T"&gt;&lt;/line&gt;&lt;g id="root-2" data-et="participant" data-type="participant" data-id="T"&gt;&lt;rect x="589" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="T" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="664" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="664" dy="0"&gt;herddeck tunnel&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor1" x1="347" y1="65" x2="347" y2="335" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="M" data-et="life-line" data-id="M"&gt;&lt;/line&gt;&lt;g id="root-1" data-et="participant" data-type="participant" data-id="M"&gt;&lt;rect x="272" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="M" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="347" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="347" dy="0"&gt;ControlMaster&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor0" x1="75" y1="65" x2="75" y2="335" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="S" data-et="life-line" data-id="S"&gt;&lt;/line&gt;&lt;g id="root-0" data-et="participant" data-type="participant" data-id="S"&gt;&lt;rect x="0" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="S" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="75" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;your shell&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;style&gt;#da14699abd285f1b6light{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#da14699abd285f1b6light .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#da14699abd285f1b6light .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#da14699abd285f1b6light .error-icon{fill:rgb(255, 255, 255);}#da14699abd285f1b6light .error-text{fill:#000000;stroke:#000000;}#da14699abd285f1b6light .edge-thickness-normal{stroke-width:1px;}#da14699abd285f1b6light .edge-thickness-thick{stroke-width:3.5px;}#da14699abd285f1b6light .edge-pattern-solid{stroke-dasharray:0;}#da14699abd285f1b6light .edge-thickness-invisible{stroke-width:0;fill:none;}#da14699abd285f1b6light .edge-pattern-dashed{stroke-dasharray:3;}#da14699abd285f1b6light .edge-pattern-dotted{stroke-dasharray:2;}#da14699abd285f1b6light .marker{fill:rgb(74, 86, 92);stroke:rgb(74, 86, 92);}#da14699abd285f1b6light .marker.cross{stroke:rgb(74, 86, 92);}#da14699abd285f1b6light svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#da14699abd285f1b6light p{margin:0;}#da14699abd285f1b6light .actor{stroke:rgb(204, 216, 222);fill:rgb(255, 255, 255);stroke-width:1;}#da14699abd285f1b6light rect.actor.outer-path[data-look="neo"]{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6light rect.note[data-look="neo"]{stroke:hsl(52.6829268293, 60%, 73.9215686275%);fill:#fff5ad;filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6light text.actor&amp;gt;tspan{fill:#333;stroke:none;}#da14699abd285f1b6light .actor-line{stroke:rgb(204, 216, 222);}#da14699abd285f1b6light .innerArc{stroke-width:1.5;stroke-dasharray:none;}#da14699abd285f1b6light .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#da14699abd285f1b6light .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#da14699abd285f1b6light [id$="-arrowhead"] path{fill:#333;stroke:#333;}#da14699abd285f1b6light .sequenceNumber{fill:#b5a9a3;}#da14699abd285f1b6light [id$="-sequencenumber"]{fill:#333;}#da14699abd285f1b6light [id$="-crosshead"] path{fill:#333;stroke:#333;}#da14699abd285f1b6light .messageText{fill:#333;stroke:none;}#da14699abd285f1b6light .labelBox{stroke:rgb(204, 216, 222);fill:rgb(255, 255, 255);filter:none;}#da14699abd285f1b6light .labelText,#da14699abd285f1b6light .labelText&amp;gt;tspan{fill:#333;stroke:none;}#da14699abd285f1b6light .loopText,#da14699abd285f1b6light .loopText&amp;gt;tspan{fill:#333;stroke:none;}#da14699abd285f1b6light .sectionTitle,#da14699abd285f1b6light .sectionTitle&amp;gt;tspan{fill:#333;stroke:none;}#da14699abd285f1b6light .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:rgb(204, 216, 222);fill:rgb(204, 216, 222);}#da14699abd285f1b6light .note{stroke:hsl(52.6829268293, 60%, 73.9215686275%);fill:#fff5ad;}#da14699abd285f1b6light .noteText,#da14699abd285f1b6light .noteText&amp;gt;tspan{fill:#333;stroke:none;font-weight:normal;}#da14699abd285f1b6light .activation0{fill:rgb(182, 240, 255);stroke:hsl(192.3287671233, 100%, 75.6862745098%);}#da14699abd285f1b6light .activation1{fill:rgb(182, 240, 255);stroke:hsl(192.3287671233, 100%, 75.6862745098%);}#da14699abd285f1b6light .activation2{fill:rgb(182, 240, 255);stroke:hsl(192.3287671233, 100%, 75.6862745098%);}#da14699abd285f1b6light .actorPopupMenu{position:absolute;}#da14699abd285f1b6light .actorPopupMenuPanel{position:absolute;fill:rgb(255, 255, 255);box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#da14699abd285f1b6light .actor-man circle,#da14699abd285f1b6light line{fill:rgb(255, 255, 255);stroke-width:2px;}#da14699abd285f1b6light g rect.rect{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));stroke:rgb(204, 216, 222);}#da14699abd285f1b6light .node .neo-node{stroke:rgb(204, 216, 222);}#da14699abd285f1b6light [data-look="neo"].node rect,#da14699abd285f1b6light [data-look="neo"].cluster rect,#da14699abd285f1b6light [data-look="neo"].node polygon{stroke:url(#da14699abd285f1b6light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6light [data-look="neo"].swimlane.cluster rect{filter:none;}#da14699abd285f1b6light [data-look="neo"].node path{stroke:url(#da14699abd285f1b6light-gradient);stroke-width:1px;}#da14699abd285f1b6light [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6light [data-look="neo"].node .neo-line path{stroke:rgb(204, 216, 222);filter:none;}#da14699abd285f1b6light [data-look="neo"].node circle{stroke:url(#da14699abd285f1b6light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6light [data-look="neo"].node circle .state-start{fill:#000000;}#da14699abd285f1b6light [data-look="neo"].icon-shape .icon{fill:url(#da14699abd285f1b6light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6light [data-look="neo"].icon-shape .icon-neo path{stroke:url(#da14699abd285f1b6light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6light :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;/g&gt;&lt;defs&gt;&lt;symbol id="da14699abd285f1b6light-computer" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="da14699abd285f1b6light-database" fill-rule="evenodd" clip-rule="evenodd"&gt;&lt;path transform="scale(.5)" d="M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="da14699abd285f1b6light-clock" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6light-arrowhead" refX="7.9" refY="5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M -1 0 L 10 5 L 0 10 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6light-crosshead" markerWidth="15" markerHeight="8" orient="auto" refX="4" refY="4.5"&gt;&lt;path fill="none" stroke="#000000" stroke-width="1pt" d="M 1,2 L 6,7 M 6,2 L 1,7" style="stroke-dasharray: 0, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6light-filled-head" refX="15.5" refY="7" markerWidth="20" markerHeight="28" orient="auto"&gt;&lt;path d="M 18,7 L9,13 L14,7 L9,1 Z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6light-sequencenumber" refX="15" refY="15" markerWidth="60" markerHeight="40" orient="auto"&gt;&lt;circle cx="15" cy="15" r="6"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6light-solidTopArrowHead" refX="7.9" refY="7.25" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 8 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6light-solidBottomArrowHead" refX="7.9" refY="0.75" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 0 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6light-stickTopArrowHead" refX="7.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 7 7" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6light-stickBottomArrowHead" refX="7.5" refY="0" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 7 L 7 0" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;g data-et="note" data-id="i2"&gt;&lt;rect x="549.5" y="167" fill="#EDF2AE" stroke="#666" width="229" height="56" class="note"&gt;&lt;/rect&gt;&lt;text x="664" y="172" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="664"&gt;tunnel now depends on&lt;/tspan&gt;&lt;/text&gt;&lt;text x="664" y="190" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="664"&gt;a connection it does not own&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;text x="210" y="80" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;ssh desktop (opens master)&lt;/text&gt;&lt;line x1="76" y1="111" x2="343" y2="111" class="messageLine0" data-et="message" data-id="i0" data-from="S" data-to="M" stroke-width="2" stroke="none" marker-end="url(#da14699abd285f1b6light-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="507" y="126" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;ssh -N -L (muxes onto master)&lt;/text&gt;&lt;line x1="663" y1="157" x2="351" y2="157" class="messageLine0" data-et="message" data-id="i1" data-from="T" data-to="M" stroke-width="2" stroke="none" marker-end="url(#da14699abd285f1b6light-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="210" y="238" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;exit&lt;/text&gt;&lt;line x1="76" y1="269" x2="343" y2="269" class="messageLine0" data-et="message" data-id="i3" data-from="S" data-to="M" stroke-width="2" stroke="none" marker-end="url(#da14699abd285f1b6light-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="504" y="284" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;ControlPersist expires, tunnel dies&lt;/text&gt;&lt;line x1="348" y1="315" x2="660" y2="315" class="messageLine1" data-et="message" data-id="i4" data-from="M" data-to="T" stroke-width="2" stroke="none" marker-end="url(#da14699abd285f1b6light-crosshead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/div&gt;&#10; &lt;div class="not-prose diagram diagram-dark"&gt;&lt;svg id="da14699abd285f1b6dark" width="100%" xmlns="http://www.w3.org/2000/svg" style="max-width: 878.5px;" viewBox="-50 -10 878.5 421" role="graphics-document document" aria-roledescription="sequence"&gt;&lt;g&gt;&lt;rect x="589" y="335" fill="#eaeaea" stroke="#666" width="150" height="65" name="T" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="664" y="367.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="664" dy="0"&gt;herddeck tunnel&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x="272" y="335" fill="#eaeaea" stroke="#666" width="150" height="65" name="M" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="347" y="367.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="347" dy="0"&gt;ControlMaster&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x="0" y="335" fill="#eaeaea" stroke="#666" width="150" height="65" name="S" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="75" y="367.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;your shell&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor5" x1="664" y1="65" x2="664" y2="335" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="T" data-et="life-line" data-id="T"&gt;&lt;/line&gt;&lt;g id="root-5" data-et="participant" data-type="participant" data-id="T"&gt;&lt;rect x="589" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="T" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="664" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="664" dy="0"&gt;herddeck tunnel&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor4" x1="347" y1="65" x2="347" y2="335" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="M" data-et="life-line" data-id="M"&gt;&lt;/line&gt;&lt;g id="root-4" data-et="participant" data-type="participant" data-id="M"&gt;&lt;rect x="272" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="M" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="347" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="347" dy="0"&gt;ControlMaster&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor3" x1="75" y1="65" x2="75" y2="335" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="S" data-et="life-line" data-id="S"&gt;&lt;/line&gt;&lt;g id="root-3" data-et="participant" data-type="participant" data-id="S"&gt;&lt;rect x="0" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="S" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="75" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;your shell&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;style&gt;#da14699abd285f1b6dark{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#ccc;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#da14699abd285f1b6dark .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#da14699abd285f1b6dark .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#da14699abd285f1b6dark .error-icon{fill:#a44141;}#da14699abd285f1b6dark .error-text{fill:#ddd;stroke:#ddd;}#da14699abd285f1b6dark .edge-thickness-normal{stroke-width:1px;}#da14699abd285f1b6dark .edge-thickness-thick{stroke-width:3.5px;}#da14699abd285f1b6dark .edge-pattern-solid{stroke-dasharray:0;}#da14699abd285f1b6dark .edge-thickness-invisible{stroke-width:0;fill:none;}#da14699abd285f1b6dark .edge-pattern-dashed{stroke-dasharray:3;}#da14699abd285f1b6dark .edge-pattern-dotted{stroke-dasharray:2;}#da14699abd285f1b6dark .marker{fill:lightgrey;stroke:lightgrey;}#da14699abd285f1b6dark .marker.cross{stroke:lightgrey;}#da14699abd285f1b6dark svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#da14699abd285f1b6dark p{margin:0;}#da14699abd285f1b6dark .actor{stroke:#ccc;fill:#1f2020;stroke-width:1;}#da14699abd285f1b6dark rect.actor.outer-path[data-look="neo"]{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6dark rect.note[data-look="neo"]{stroke:hsl(180, 0%, 18.3529411765%);fill:hsl(180, 1.5873015873%, 28.3529411765%);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6dark text.actor&amp;gt;tspan{fill:lightgrey;stroke:none;}#da14699abd285f1b6dark .actor-line{stroke:#ccc;}#da14699abd285f1b6dark .innerArc{stroke-width:1.5;stroke-dasharray:none;}#da14699abd285f1b6dark .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:lightgrey;}#da14699abd285f1b6dark .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:lightgrey;}#da14699abd285f1b6dark [id$="-arrowhead"] path{fill:lightgrey;stroke:lightgrey;}#da14699abd285f1b6dark .sequenceNumber{fill:black;}#da14699abd285f1b6dark [id$="-sequencenumber"]{fill:lightgrey;}#da14699abd285f1b6dark [id$="-crosshead"] path{fill:lightgrey;stroke:lightgrey;}#da14699abd285f1b6dark .messageText{fill:lightgrey;stroke:none;}#da14699abd285f1b6dark .labelBox{stroke:#ccc;fill:#1f2020;filter:none;}#da14699abd285f1b6dark .labelText,#da14699abd285f1b6dark .labelText&amp;gt;tspan{fill:lightgrey;stroke:none;}#da14699abd285f1b6dark .loopText,#da14699abd285f1b6dark .loopText&amp;gt;tspan{fill:lightgrey;stroke:none;}#da14699abd285f1b6dark .sectionTitle,#da14699abd285f1b6dark .sectionTitle&amp;gt;tspan{fill:lightgrey;stroke:none;}#da14699abd285f1b6dark .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:#ccc;fill:#ccc;}#da14699abd285f1b6dark .note{stroke:hsl(180, 0%, 18.3529411765%);fill:hsl(180, 1.5873015873%, 28.3529411765%);}#da14699abd285f1b6dark .noteText,#da14699abd285f1b6dark .noteText&amp;gt;tspan{fill:rgb(183.8476190475, 181.5523809523, 181.5523809523);stroke:none;font-weight:normal;}#da14699abd285f1b6dark .activation0{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:#ccc;}#da14699abd285f1b6dark .activation1{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:#ccc;}#da14699abd285f1b6dark .activation2{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:#ccc;}#da14699abd285f1b6dark .actorPopupMenu{position:absolute;}#da14699abd285f1b6dark .actorPopupMenuPanel{position:absolute;fill:#1f2020;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#da14699abd285f1b6dark .actor-man circle,#da14699abd285f1b6dark line{fill:#1f2020;stroke-width:2px;}#da14699abd285f1b6dark g rect.rect{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));stroke:#ccc;}#da14699abd285f1b6dark .node .neo-node{stroke:#ccc;}#da14699abd285f1b6dark [data-look="neo"].node rect,#da14699abd285f1b6dark [data-look="neo"].cluster rect,#da14699abd285f1b6dark [data-look="neo"].node polygon{stroke:url(#da14699abd285f1b6dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6dark [data-look="neo"].swimlane.cluster rect{filter:none;}#da14699abd285f1b6dark [data-look="neo"].node path{stroke:url(#da14699abd285f1b6dark-gradient);stroke-width:1px;}#da14699abd285f1b6dark [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6dark [data-look="neo"].node .neo-line path{stroke:#ccc;filter:none;}#da14699abd285f1b6dark [data-look="neo"].node circle{stroke:url(#da14699abd285f1b6dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6dark [data-look="neo"].node circle .state-start{fill:#000000;}#da14699abd285f1b6dark [data-look="neo"].icon-shape .icon{fill:url(#da14699abd285f1b6dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6dark [data-look="neo"].icon-shape .icon-neo path{stroke:url(#da14699abd285f1b6dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#da14699abd285f1b6dark :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;/g&gt;&lt;defs&gt;&lt;symbol id="da14699abd285f1b6dark-computer" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="da14699abd285f1b6dark-database" fill-rule="evenodd" clip-rule="evenodd"&gt;&lt;path transform="scale(.5)" d="M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="da14699abd285f1b6dark-clock" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6dark-arrowhead" refX="7.9" refY="5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M -1 0 L 10 5 L 0 10 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6dark-crosshead" markerWidth="15" markerHeight="8" orient="auto" refX="4" refY="4.5"&gt;&lt;path fill="none" stroke="#000000" stroke-width="1pt" d="M 1,2 L 6,7 M 6,2 L 1,7" style="stroke-dasharray: 0, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6dark-filled-head" refX="15.5" refY="7" markerWidth="20" markerHeight="28" orient="auto"&gt;&lt;path d="M 18,7 L9,13 L14,7 L9,1 Z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6dark-sequencenumber" refX="15" refY="15" markerWidth="60" markerHeight="40" orient="auto"&gt;&lt;circle cx="15" cy="15" r="6"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6dark-solidTopArrowHead" refX="7.9" refY="7.25" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 8 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6dark-solidBottomArrowHead" refX="7.9" refY="0.75" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 0 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6dark-stickTopArrowHead" refX="7.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 7 7" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="da14699abd285f1b6dark-stickBottomArrowHead" refX="7.5" refY="0" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 7 L 7 0" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;g data-et="note" data-id="i2"&gt;&lt;rect x="549.5" y="167" fill="#EDF2AE" stroke="#666" width="229" height="56" class="note"&gt;&lt;/rect&gt;&lt;text x="664" y="172" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="664"&gt;tunnel now depends on&lt;/tspan&gt;&lt;/text&gt;&lt;text x="664" y="190" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="664"&gt;a connection it does not own&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;text x="210" y="80" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;ssh desktop (opens master)&lt;/text&gt;&lt;line x1="76" y1="111" x2="343" y2="111" class="messageLine0" data-et="message" data-id="i0" data-from="S" data-to="M" stroke-width="2" stroke="none" marker-end="url(#da14699abd285f1b6dark-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="507" y="126" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;ssh -N -L (muxes onto master)&lt;/text&gt;&lt;line x1="663" y1="157" x2="351" y2="157" class="messageLine0" data-et="message" data-id="i1" data-from="T" data-to="M" stroke-width="2" stroke="none" marker-end="url(#da14699abd285f1b6dark-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="210" y="238" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;exit&lt;/text&gt;&lt;line x1="76" y1="269" x2="343" y2="269" class="messageLine0" data-et="message" data-id="i3" data-from="S" data-to="M" stroke-width="2" stroke="none" marker-end="url(#da14699abd285f1b6dark-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="504" y="284" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;ControlPersist expires, tunnel dies&lt;/text&gt;&lt;line x1="348" y1="315" x2="660" y2="315" class="messageLine1" data-et="message" data-id="i4" data-from="M" data-to="T" stroke-width="2" stroke="none" marker-end="url(#da14699abd285f1b6dark-crosshead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/div&gt;&#10;&lt;p&gt;Direction one: the tunnel dies when an unrelated master connection closes, or when its &lt;code&gt;ControlPersist&lt;/code&gt; timer expires. Your daemon&amp;rsquo;s connectivity is now coupled to whether you happen to have a terminal open.&lt;/p&gt;&#10;&lt;p&gt;Direction two: if the tunnel gets there first, &lt;em&gt;it&lt;/em&gt; becomes the master. Every subsequent interactive &lt;code&gt;ssh&lt;/code&gt; to that host attaches to a connection owned by a background daemon. Restart the daemon and you drop the shells of anyone attached to it.&lt;/p&gt;&#10;&lt;p&gt;The fix is to refuse to participate:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// Own the connection lifecycle: with the user&amp;#39;s ControlMaster&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// auto config the tunnel would otherwise mux onto (or become) a&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// shared master and its forward would die with that master&amp;#39;s&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ControlPersist timer (found live in Tier-1 testing).&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;-o&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ControlMaster=no&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;-o&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ControlPath=none&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;-o&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ExitOnForwardFailure=yes&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;A tunnel must own its connection. Borrowing one couples its lifetime to something the daemon does not control and cannot observe.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Not every failure deserves a retry&#10; &lt;div id="not-every-failure-deserves-a-retry" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#not-every-failure-deserves-a-retry" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;launchd starts the daemon at login, which on a laptop means before Wi-Fi has associated and well before a VPN is up. The first tunnel attempt fails, and in the original code that failure was permanent until I restarted the daemon by hand.&lt;/p&gt;&#10;&lt;p&gt;Retrying everything is the wrong correction. Retrying a wrong passphrase forever is how you get an account locked out, and retrying a host-key mismatch forever is how you sit in a loop ignoring a security warning. So failures get classified by what ssh said:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;ssh stderr contains&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Class&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Behaviour&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Permission denied&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;permanent&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;no retry, surfaced as &lt;code&gt;auth&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Host key verification failed&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;permanent&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;no retry, surfaced as &lt;code&gt;auth&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Bad configuration&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;permanent&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;no retry&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Bad owner or permissions&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;permanent&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;no retry&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;everything else&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;transient&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;capped jittered retry, 15s to 10min&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Connection refused, timeout, network unreachable and DNS resolution failures all default to transient, and DNS is the interesting one. &lt;code&gt;Could not resolve hostname&lt;/code&gt; looks permanent and is completely ambiguous under a VPN that has not come up yet. The default is to retry, because the cost of retrying a genuinely dead hostname is a log line every ten minutes, and the cost of not retrying is a daemon that is dead until you notice.&lt;/p&gt;&#10;&lt;p&gt;The loud error fires once per target rather than once per retry, which is the difference between a diagnostic and a log flood.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;localhost is a valid far side&#10; &lt;div id="localhost-is-a-valid-far-side" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#localhost-is-a-valid-far-side" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;The technique that made most of this testable, and the part I would steal if I were reading someone else&amp;rsquo;s post:&lt;/p&gt;&#10;&lt;p&gt;&amp;ldquo;Remote&amp;rdquo; here means the far side of an ssh streamlocal forward. It does not mean &amp;ldquo;a different machine&amp;rdquo;. Point the forward at &lt;code&gt;localhost&lt;/code&gt; and you get a genuine SSH forward, with a genuine remote sshd, a genuine second herdr socket path, and genuine channel-open failures, on one laptop with no second machine and no network.&lt;/p&gt;&#10;&lt;p&gt;Wrong path, remote daemon down, socket missing, sshd refusing connections, first-connect-before-network: all of those reproduce against sshd on localhost. Only latency and actual link loss need real hardware.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;One caveat about &lt;code&gt;--remote&lt;/code&gt;, because the obvious guess is wrong&#10; &lt;div id="one-caveat-about---remote-because-the-obvious-guess-is-wrong" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#one-caveat-about---remote-because-the-obvious-guess-is-wrong" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;herdr has a &lt;code&gt;--remote&lt;/code&gt; flag and it does not do what you would assume from the outside.&lt;/p&gt;&#10;&lt;p&gt;It makes the &lt;em&gt;human&lt;/em&gt; terminal experience a thin client. It does not expose a local API socket that proxies the remote server. The local &lt;code&gt;herdr-client.sock&lt;/code&gt; is a TUI attach endpoint, so a daemon cannot piggyback on it and needs its own forward.&lt;/p&gt;&#10;&lt;p&gt;There is a consequence to that which bit me later. herdr auto-syncs versions between client and server, and that sync fires on a human &lt;code&gt;--remote&lt;/code&gt; attach. It never fires for the daemon&amp;rsquo;s forward. So a machine you rarely attach to by hand can quietly drift to an older protocol while the daemon happily keeps talking to it. Every target is therefore pinged and version-checked on connect and degraded to a warning state on mismatch, rather than assumed compatible.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;When not to guess&#10; &lt;div id="when-not-to-guess" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#when-not-to-guess" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;I want to end on the one place in this system where the correct behaviour is to do nothing, because multi-target is what creates it.&lt;/p&gt;&#10;&lt;p&gt;Each herdr server tracks its own focused pane. With two targets connected, two panes can each legitimately report that the user is looking at them, and the daemon has no way to know which screen you are actually in front of.&lt;/p&gt;&#10;&lt;p&gt;The physical Enter key sends a keystroke to the focused agent. Guessing wrong there does not display something incorrect. It submits someone else&amp;rsquo;s prompt, to a different agent, on a different machine, with whatever half-written text was sitting in that pane.&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;getFocusedAgent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AgentSnapshot&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focused&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;agentsList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;find&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;sameAgent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focused&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;herdrFocused&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;agentsList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;focused&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// More than one target can each report a focused pane; only an&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// unambiguous single candidate is safe to act on.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;herdrFocused&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;herdrFocused&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;An explicit slot press always wins, because that is you telling it. Absent that, one candidate is a fallback and two candidates is nothing. The refusal has its own test, with the reasoning written into the test body, because it is the kind of behaviour a future refactor would happily &amp;ldquo;fix&amp;rdquo; into a &lt;code&gt;[0]&lt;/code&gt;.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Lessons&#10; &lt;div id="lessons" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lessons" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;A bound socket is not a connected socket. &lt;code&gt;ssh -L&lt;/code&gt; binds before anything reaches the far end, so the file existing proves only that ssh started.&lt;/li&gt;&#10;&lt;li&gt;End every &amp;ldquo;establish&amp;rdquo; with a probe that exercises the thing you are claiming works. Reachability is the claim, so make a round trip and let that be the definition.&lt;/li&gt;&#10;&lt;li&gt;If a process fails asynchronously on stderr, drain stderr for its whole life. Reading it only at startup means the exact bytes naming your bug are being written and discarded while you debug.&lt;/li&gt;&#10;&lt;li&gt;A long-lived tunnel must own its SSH connection. Muxing onto a user&amp;rsquo;s ControlMaster couples the daemon to a terminal, and winning the race to become the master is worse, because now your restart drops their shells.&lt;/li&gt;&#10;&lt;li&gt;Classify failures before retrying them. Auth and host-key failures should never retry; timeouts, refusals and DNS should, because DNS under a sleeping VPN is indistinguishable from DNS against a dead host.&lt;/li&gt;&#10;&lt;li&gt;Log the loud error once per target, not once per retry. A diagnostic that repeats every fifteen seconds is a log flood that people filter out.&lt;/li&gt;&#10;&lt;li&gt;localhost is a valid far side. Most of a remote-path test matrix runs against sshd on the same machine, and only latency and link loss need real hardware.&lt;/li&gt;&#10;&lt;li&gt;When ambiguity could take a destructive action, do nothing and test that you do nothing. Sending Enter to the wrong agent submits someone else&amp;rsquo;s prompt.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;References&#10; &lt;div id="references" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#references" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;ssh_config(5)&lt;/code&gt; for &lt;code&gt;ControlMaster&lt;/code&gt;, &lt;code&gt;ControlPath&lt;/code&gt;, &lt;code&gt;ControlPersist&lt;/code&gt; and &lt;code&gt;ExitOnForwardFailure&lt;/code&gt;; &lt;code&gt;ssh(1)&lt;/code&gt; for &lt;code&gt;-L&lt;/code&gt; and streamlocal forwarding&lt;/li&gt;&#10;&lt;li&gt;OpenSSH release notes on Unix-domain socket forwarding: &lt;a href="https://www.openssh.com/txt/release-6.7" target="_blank" rel="noreferrer"&gt;https://www.openssh.com/txt/release-6.7&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;herdr: &lt;a href="https://herdr.dev" target="_blank" rel="noreferrer"&gt;https://herdr.dev&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The rebuild that made remote almost free: &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" &gt;I deleted six subsystems by swapping one dependency.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;HerdDeck tunnel manager, ssh args, stderr ring and the probe: &lt;a href="https://github.com/nickboy/herddeck/blob/main/packages/daemon/src/tunnel.ts" target="_blank" rel="noreferrer"&gt;&lt;code&gt;packages/daemon/src/tunnel.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;HerdDeck registry, retry classification and backoff: &lt;a href="https://github.com/nickboy/herddeck/blob/main/packages/daemon/src/registry.ts" target="_blank" rel="noreferrer"&gt;&lt;code&gt;packages/daemon/src/registry.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Remote-path static review findings: &lt;a href="https://github.com/nickboy/herddeck/blob/main/docs/plans/2026-08-08-remote-path-review.md" target="_blank" rel="noreferrer"&gt;&lt;code&gt;docs/plans/2026-08-08-remote-path-review.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>The ring was 1.00:1 against its background. The key read 35% at 65%.</title><link>https://nick-liu.com/posts/herddeck-ring-read-backwards/</link><pubDate>Wed, 02 Sep 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/herddeck-ring-read-backwards/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;A key on my Stream Deck showed an agent with a context window 65% full. The ring around it displayed a little over a third of a circle, which anyone would read as 35%. Not blank, not obviously broken, no missing-data placeholder. Confidently wrong, by exactly the amount that inverts the decision you are making when you look at it.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 4 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me."&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question."&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other."&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%." aria-current="step"&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end."&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;herdr&lt;/strong&gt; &lt;code&gt;0.8.0&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;figure&gt;&lt;picture&gt;&#10; &lt;source&#10; type="image/webp"&#10; srcset="https://nick-liu.com/img/herddeck-page-1_hu_603b2fa275025503.webp 800w, https://nick-liu.com/img/herddeck-page-1_hu_36eb5909d6118535.webp 1280w"&#10; sizes="(min-width: 768px) 50vw, 65vw"&gt;&#10; &lt;img&#10; class="my-0 rounded-md"&#10; loading="lazy"&#10; decoding="async"&#10; fetchpriority="auto"&#10; alt="HerdDeck page 1 on a Stream Deck MK.2. The top row is five agent slots, each tinted by lifecycle state with a context-fullness ring drawn around it; the first slot is highlighted because that agent is working"&#10; width="940"&#10; height="530"&#10; src="https://nick-liu.com/img/herddeck-page-1_hu_f75716e8a2a14490.png"&#10; srcset="https://nick-liu.com/img/herddeck-page-1_hu_f75716e8a2a14490.png 800w, https://nick-liu.com/img/herddeck-page-1.png 1280w"&#10; sizes="(min-width: 768px) 50vw, 65vw"&#10; data-zoom-src="https://nick-liu.com/img/herddeck-page-1.png"&gt;&#10; &lt;/picture&gt;&lt;/figure&gt;&#10;&lt;p&gt;That is the hardware, live against five agents on a remote machine. The top row is one key per agent. The tint is that session&amp;rsquo;s lifecycle state, and the ring is how full its context window is. Two independent signals, one key.&lt;/p&gt;</description><content:encoded>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;A key on my Stream Deck showed an agent with a context window 65% full. The ring around it displayed a little over a third of a circle, which anyone would read as 35%. Not blank, not obviously broken, no missing-data placeholder. Confidently wrong, by exactly the amount that inverts the decision you are making when you look at it.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 4 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me."&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question."&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other."&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%." aria-current="step"&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end."&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;herdr&lt;/strong&gt; &lt;code&gt;0.8.0&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;figure&gt;&lt;picture&gt;&#10; &lt;source&#10; type="image/webp"&#10; srcset="https://nick-liu.com/img/herddeck-page-1_hu_603b2fa275025503.webp 800w, https://nick-liu.com/img/herddeck-page-1_hu_36eb5909d6118535.webp 1280w"&#10; sizes="(min-width: 768px) 50vw, 65vw"&gt;&#10; &lt;img&#10; class="my-0 rounded-md"&#10; loading="lazy"&#10; decoding="async"&#10; fetchpriority="auto"&#10; alt="HerdDeck page 1 on a Stream Deck MK.2. The top row is five agent slots, each tinted by lifecycle state with a context-fullness ring drawn around it; the first slot is highlighted because that agent is working"&#10; width="940"&#10; height="530"&#10; src="https://nick-liu.com/img/herddeck-page-1_hu_f75716e8a2a14490.png"&#10; srcset="https://nick-liu.com/img/herddeck-page-1_hu_f75716e8a2a14490.png 800w, https://nick-liu.com/img/herddeck-page-1.png 1280w"&#10; sizes="(min-width: 768px) 50vw, 65vw"&#10; data-zoom-src="https://nick-liu.com/img/herddeck-page-1.png"&gt;&#10; &lt;/picture&gt;&lt;/figure&gt;&#10;&lt;p&gt;That is the hardware, live against five agents on a remote machine. The top row is one key per agent. The tint is that session&amp;rsquo;s lifecycle state, and the ring is how full its context window is. Two independent signals, one key.&lt;/p&gt;&#10;&lt;p&gt;That sentence is the bug. It took me three weeks and an adversarial review to hear it.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;It started as a text bug&#10; &lt;div id="it-started-as-a-text-bug" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#it-started-as-a-text-bug" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;The slot titles were hardcoded &lt;code&gt;fill=&amp;quot;#fff&amp;quot;&lt;/code&gt;. White text on every status colour, and the status colours are Catppuccin Mocha accents, half of which are pale.&lt;/p&gt;&#10;&lt;p&gt;I measured every combination. These numbers I have recomputed from the palette hexes for this post rather than quoting the ones in my notes, which turned out not to survive checking:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Status&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Background&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;White text&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Dark text&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;working&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;#f9e2af&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;1.27:1&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;12.91:1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;done&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;#a6e3a1&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;1.49:1&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;11.03:1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;blocked&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;#f38ba8&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;2.32:1&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;7.08:1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;idle&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;#6c7086&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;4.88:1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;3.36:1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;offline&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;#45475a&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;9.12:1&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1.80:1&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;div class="chart"&gt;&#10; &lt;canvas data-chart="&#10;type: &amp;#39;bar&amp;#39;,&#10;data: {&#10; labels: [&amp;#39;working&amp;#39;, &amp;#39;done&amp;#39;, &amp;#39;blocked&amp;#39;, &amp;#39;idle&amp;#39;, &amp;#39;offline&amp;#39;],&#10; datasets: [&#10; { label: &amp;#39;white text&amp;#39;, data: [1.27, 1.49, 2.32, 4.88, 9.12],&#10; backgroundColor: &amp;#39;#f43f5e&amp;#39;, borderRadius: 4 },&#10; { label: &amp;#39;dark text (#1e1e2e)&amp;#39;, data: [12.91, 11.03, 7.08, 3.36, 1.80],&#10; backgroundColor: &amp;#39;#10b981&amp;#39;, borderRadius: 4 }&#10; ]&#10;},&#10;options: {&#10; responsive: true,&#10; indexAxis: &amp;#39;y&amp;#39;,&#10; categoryPercentage: 0.7,&#10; barPercentage: 0.8,&#10; plugins: {&#10; legend: { labels: { color: &amp;#39;#a1a1aa&amp;#39; } },&#10; title: { display: true, text: &amp;#39;Contrast ratio against each status background (WCAG AA for normal text is 4.5:1)&amp;#39;, color: &amp;#39;#a1a1aa&amp;#39; }&#10; },&#10; scales: {&#10; x: { title: { display: true, text: &amp;#39;Contrast ratio (higher is better)&amp;#39;, color: &amp;#39;#a1a1aa&amp;#39; },&#10; ticks: { color: &amp;#39;#a1a1aa&amp;#39; }, grid: { color: &amp;#39;rgba(255,255,255,0.08)&amp;#39; } },&#10; y: { ticks: { color: &amp;#39;#a1a1aa&amp;#39; }, grid: { color: &amp;#39;rgba(255,255,255,0.08)&amp;#39; } }&#10; }&#10;}&#10;"&gt;&lt;/canvas&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;WCAG AA for normal text is 4.5:1. Three of five statuses failed, and &lt;code&gt;working&lt;/code&gt;, at 1.27:1, is effectively invisible. That is the state you most need to read at a glance, because it is the one that tells you an agent is busy rather than waiting on you.&lt;/p&gt;&#10;&lt;p&gt;Note the last two rows. White is genuinely correct on the dark half of the palette. So the fix is not &amp;ldquo;swap white for dark&amp;rdquo;, it is &amp;ldquo;pick per background&amp;rdquo;:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;inkFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backgroundHex&lt;/span&gt;: &lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;contrastRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backgroundHex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;INK_DARK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;contrastRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;backgroundHex&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;INK_LIGHT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;INK_DARK&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; : &lt;span class="kt"&gt;INK_LIGHT&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;The status colours themselves are unchanged, so the colour coding still means what it meant. A parameterised test asserts every status clears AA, driven off the palette object itself, so adding a status generates its own case.&lt;/p&gt;&#10;&lt;p&gt;I shipped that, titled the commit &amp;ldquo;make slot text readable&amp;rdquo;, and wrote in the message that the new test meant a future palette change could not quietly reintroduce the problem.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The review said I had fixed the wrong surface&#10; &lt;div id="the-review-said-i-had-fixed-the-wrong-surface" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-review-said-i-had-fixed-the-wrong-surface" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;I asked for an adversarial review of that work. The first finding was that the commit fixed the text on a key whose dominant graphic is the ring, and the ring was worse.&lt;/p&gt;&#10;&lt;p&gt;Worse, and untested. The commit message claimed a guarantee about future palette changes. That guarantee was real for the text and did not exist for the ring, because the test never covered the ring. I had written a confident sentence about a protection I had not built.&lt;/p&gt;&#10;&lt;p&gt;The ring took its colour from &lt;code&gt;thresholdColor(pct)&lt;/code&gt;, green under 50%, yellow to 79%, red above, drawn straight onto the status-coloured background. Two palettes, both Catppuccin accents, on the same surface.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Three collisions, and only two were the same colour&#10; &lt;div id="three-collisions-and-only-two-were-the-same-colour" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#three-collisions-and-only-two-were-the-same-colour" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;div class="flex justify-between"&gt;&#10; &lt;span&#10; class="w-full py-6 mr-3 rounded-md"&#10; style="background-color: #f9e2af"&gt;&lt;/span&gt;&#10; &lt;span&#10; class="w-full py-6 mr-3 rounded-md"&#10; style="background-color: #f38ba8"&gt;&lt;/span&gt;&#10; &lt;span&#10; class="w-full py-6 mr-3 rounded-md"&#10; style="background-color: #a6e3a1"&gt;&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;Those are &lt;code&gt;working&lt;/code&gt;, &lt;code&gt;blocked&lt;/code&gt; and &lt;code&gt;done&lt;/code&gt;. The threshold palette is &lt;code&gt;#94e2d5&lt;/code&gt;, &lt;code&gt;#f9e2af&lt;/code&gt;, &lt;code&gt;#f38ba8&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Two of the collisions are trivial once you line the hexes up:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;THRESHOLD_YELLOW === STATUS_COLOURS.working // &amp;#34;#f9e2af&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;THRESHOLD_RED === STATUS_COLOURS.blocked // &amp;#34;#f38ba8&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Same string. A yellow ring on a yellow key, a red ring on a red key.&lt;/p&gt;&#10;&lt;p&gt;The third one is the one I missed, and it is more interesting. &lt;code&gt;done&lt;/code&gt; is &lt;code&gt;#a6e3a1&lt;/code&gt; and &lt;code&gt;THRESHOLD_GREEN&lt;/code&gt; is &lt;code&gt;#94e2d5&lt;/code&gt;. Those are not the same colour. One is a green and the other is a teal, and side by side you can tell them apart immediately.&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#a6e3a1 relative luminance 0.65618&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;#94e2d5 relative luminance 0.65493&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Contrast ratio is a ratio of relative luminances. Hue does not enter into it. Two colours that differ obviously to the eye can be arithmetically indistinguishable to the contrast formula, and these differ by roughly one part in five hundred, which rounds to 1.00:1.&lt;/p&gt;&#10;&lt;p&gt;Across all eighteen ring-and-background pairs I get &lt;strong&gt;eleven under the 3:1 floor for non-text graphics, and three at exactly 1.00:1&lt;/strong&gt;, on &lt;code&gt;working&lt;/code&gt;, &lt;code&gt;blocked&lt;/code&gt; and &lt;code&gt;done&lt;/code&gt;. My original notes said nine under 3:1, which is why I recomputed everything for this post. If you are going to publish a table criticising a palette, run the numbers again first.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Invisible would have been the good outcome&#10; &lt;div id="invisible-would-have-been-the-good-outcome" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#invisible-would-have-been-the-good-outcome" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;This is where a contrast bug turns into a correctness bug.&lt;/p&gt;&#10;&lt;p&gt;A ring is drawn as two arcs. The filled arc is the percentage. The unfilled remainder is a track, and the track is not the background, it is &lt;code&gt;stroke=&amp;quot;#000&amp;quot;&lt;/code&gt; at &lt;code&gt;stroke-opacity=&amp;quot;0.35&amp;quot;&lt;/code&gt;, a dark groove so you can see where the ring would go.&lt;/p&gt;&#10;&lt;p&gt;Set the filled arc to the same colour as the background and it disappears into the background. The track does not. It is still a dark arc.&lt;/p&gt;&#10;&lt;p&gt;So a &lt;code&gt;working&lt;/code&gt; agent at 65% context lost its filled 65% and kept its empty 35%, and what you saw was a single dark arc covering a bit more than a third of the circle.&lt;/p&gt;&#10;&lt;p&gt;Not blank. Not a question mark. A confident, readable, wrong number, and inverted in the specific sense that it reported the complement. &lt;code&gt;working&lt;/code&gt; and &lt;code&gt;blocked&lt;/code&gt; are the two states where you are actively deciding whether to intervene, and both of them lied.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The fix deleted a signal&#10; &lt;div id="the-fix-deleted-a-signal" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-fix-deleted-a-signal" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;The instinct is to add something: a border, an outline, a different threshold palette tuned for contrast against each background. All of that is more code defending a design that was wrong.&lt;/p&gt;&#10;&lt;p&gt;Arc length already encodes the percentage. Hue was re-encoding the same number, coarser, in the one channel the background had already claimed. So the ring now draws in &lt;code&gt;inkFor(backgroundHex)&lt;/code&gt;, the same ink as the text:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ink&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;inkFor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;backgroundHex&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;That resolves the collision by construction and inherits the text&amp;rsquo;s contrast guarantee for free, which means a future palette edit cannot bring it back. Post-fix, the filled arc against its own track measures 3.23:1 on &lt;code&gt;blocked&lt;/code&gt;, 4.75:1 on &lt;code&gt;done&lt;/code&gt;, 5.43:1 on &lt;code&gt;working&lt;/code&gt;, 8.93:1 on &lt;code&gt;idle&lt;/code&gt; and 13.41:1 on &lt;code&gt;offline&lt;/code&gt;. All clear of the 3:1 non-text floor, and the test asserts it per status.&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;thresholdColor&lt;/code&gt; still exists and still earns its place on the Plan Usage key, whose background is a neutral constant. The function was never the problem. Drawing it onto a surface that already owned that channel was.&lt;/p&gt;&#10;&lt;p&gt;The general lesson has nothing to do with colour. Two independent signals were competing for one channel on one surface, and the symptom of that competition was not &amp;ldquo;hard to read&amp;rdquo;. It was &amp;ldquo;reads as a different number&amp;rdquo;.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The near-miss underneath&#10; &lt;div id="the-near-miss-underneath" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-near-miss-underneath" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;One more, because it makes the same point a third time and I only caught it because the tests were parameterised by then.&lt;/p&gt;&#10;&lt;p&gt;An intermediate version drew the text at &lt;code&gt;fill-opacity=&amp;quot;0.75&amp;quot;&lt;/code&gt; for a softer look. The suite was measuring the ink against the background and reporting 7.08:1 on &lt;code&gt;blocked&lt;/code&gt; and 4.88:1 on &lt;code&gt;idle&lt;/code&gt;, both passing. Composite the actual 75% ink over the actual background and you get &lt;strong&gt;4.40:1 and 3.54:1&lt;/strong&gt;. Both below AA. The suite was measuring a colour that was never drawn.&lt;/p&gt;&#10;&lt;p&gt;There is now a test asserting no partially-transparent text, which is a blunter rule than &amp;ldquo;composite before measuring&amp;rdquo; and the right one for a surface this small.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Lessons&#10; &lt;div id="lessons" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lessons" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;One surface, one channel, one signal. If two independent values both want colour on the same graphic, one of them has to move to position, length, or a different element.&lt;/li&gt;&#10;&lt;li&gt;A contrast collision on a partially-filled indicator does not produce a blank. It produces the complement, because the unfilled track survives. Confidently wrong beats obviously broken in every way except the one that matters.&lt;/li&gt;&#10;&lt;li&gt;Contrast ratio is a luminance ratio and knows nothing about hue. Two colours you can tell apart instantly can be 1.00:1 to the arithmetic and to anyone glancing at a 72-pixel key.&lt;/li&gt;&#10;&lt;li&gt;Fix the dominant graphic first. I shipped a fix for the text on a key whose main feature is a ring, and the commit message was more confident than the diff.&lt;/li&gt;&#10;&lt;li&gt;If a commit message claims a guarantee, point at the test that provides it. Mine claimed a future palette change could not reintroduce the bug, and the test that would have made that true did not exist.&lt;/li&gt;&#10;&lt;li&gt;Prefer deleting a redundant encoding to defending it. Arc length already carried the number; the colour was a second, coarser copy sitting in a channel that was already taken.&lt;/li&gt;&#10;&lt;li&gt;Measure the pixels you actually draw. Opacity, gradients and overlays all mean the colour in your test is not the colour on the screen.&lt;/li&gt;&#10;&lt;li&gt;A documented design system does not give you contrast for free. Catppuccin Mocha is carefully built, and nothing in it stops you putting its lightest yellow behind white text.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;References&#10; &lt;div id="references" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#references" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;WCAG 2.2 contrast minimum for text, 4.5:1: &lt;a href="https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html" target="_blank" rel="noreferrer"&gt;https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;WCAG 2.2 non-text contrast, 3:1 for graphical objects and UI components: &lt;a href="https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html" target="_blank" rel="noreferrer"&gt;https://www.w3.org/WAI/WCAG22/Understanding/non-text-contrast.html&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;Catppuccin Mocha palette: &lt;a href="https://github.com/catppuccin/catppuccin" target="_blank" rel="noreferrer"&gt;https://github.com/catppuccin/catppuccin&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The rebuild these keys belong to: &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" &gt;I deleted six subsystems by swapping one dependency.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The other thing that review caught: &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" &gt;My backoff logged errors=1 492 times.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;My earlier post on this palette: &lt;a href="https://nick-liu.com/posts/catppuccin-mocha-theming/" &gt;Catppuccin Mocha theming&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;HerdDeck slot rendering, &lt;code&gt;inkFor&lt;/code&gt; and the ring: &lt;a href="https://github.com/nickboy/herddeck/blob/main/packages/plugin/src/actions/agentSlot.ts" target="_blank" rel="noreferrer"&gt;&lt;code&gt;packages/plugin/src/actions/agentSlot.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The parameterised contrast tests: &lt;a href="https://github.com/nickboy/herddeck/blob/main/packages/plugin/src/actions/agentSlot.test.ts" target="_blank" rel="noreferrer"&gt;&lt;code&gt;packages/plugin/src/actions/agentSlot.test.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>Every context ring read 10%. Two bugs, and fixing one hid the other.</title><link>https://nick-liu.com/posts/herddeck-frozen-context-ring/</link><pubDate>Tue, 01 Sep 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/herddeck-frozen-context-ring/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;Each agent key on the Stream Deck draws a ring showing how full that session's context window is. Mine sat at 10% for days. Every session, regardless of activity, regardless of how long the agent had been grinding. I found the bug, fixed it, watched agents start reporting correct percentages every single turn, and the ring still said 10%. That was the good part, because it meant there were two.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 3 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me."&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question."&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other." aria-current="step"&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%."&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end."&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;Claude Code&lt;/strong&gt; &lt;code&gt;2.1.x&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;Ten percent is a suspicious number. Not zero, which would say &amp;ldquo;nothing ever arrived&amp;rdquo;. Not a plausible-looking 37%, which would say &amp;ldquo;this works and your session is small&amp;rdquo;. Ten percent is round, and round numbers in a display that should be noisy mean the display is not reading anything.&lt;/p&gt;</description><content:encoded>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;Each agent key on the Stream Deck draws a ring showing how full that session's context window is. Mine sat at 10% for days. Every session, regardless of activity, regardless of how long the agent had been grinding. I found the bug, fixed it, watched agents start reporting correct percentages every single turn, and the ring still said 10%. That was the good part, because it meant there were two.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 3 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me."&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question."&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other." aria-current="step"&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%."&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end."&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;Claude Code&lt;/strong&gt; &lt;code&gt;2.1.x&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;Ten percent is a suspicious number. Not zero, which would say &amp;ldquo;nothing ever arrived&amp;rdquo;. Not a plausible-looking 37%, which would say &amp;ldquo;this works and your session is small&amp;rdquo;. Ten percent is round, and round numbers in a display that should be noisy mean the display is not reading anything.&lt;/p&gt;&#10;&lt;p&gt;It turned out to be an orphan. Something wrote 10% into the cache a long time ago and nothing had overwritten it since, on any session, for days.&lt;/p&gt;&#10;&lt;ol class="border-s-2 border-primary-500 dark:border-primary-300 list-none"&gt;&#10;&#10;&#10;&#10;&#10;&#10;&lt;li&gt;&#10; &lt;div class="flex"&gt;&#10; &lt;div&#10; class="bg-primary-500 dark:bg-primary-300 text-neutral-50 dark:text-neutral-700 min-w-[30px] h-8 text-2xl flex items-center justify-center rounded-full ltr:-ml-12 rtl:-mr-[79px] mt-5"&gt;&#10; &lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"&gt;&lt;path fill="currentColor" d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/&gt;&lt;/svg&gt;&#10;&lt;/span&gt;&#10; &lt;/div&gt;&#10; &lt;div class="block p-6 rounded-lg shadow-2xl flex-1 ms-6 mb-10 break-words"&gt;&#10; &lt;div class="flex justify-between"&gt;&#10; &#10; &lt;h2 class="mt-0"&gt;Symptom&lt;/h2&gt;&#10; &#10; &#10; &lt;h3 class=""&gt;&#10; &lt;span class="flex cursor-pointer"&gt;&#10; &#10; &#10; &#10; &#10; &lt;span&#10; class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400"&gt;&#10; &#10; day 1&#10; &lt;/span&gt;&#10;&lt;/span&gt;&#10;&#10; &lt;/h3&gt;&#10; &#10; &lt;/div&gt;&#10; &#10; &lt;div class="mb-6"&gt;&#10; Ring pinned at 10% on every agent slot. Suspiciously round.&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/li&gt;&#10;&#10;&#10;&#10;&#10;&#10;&lt;li&gt;&#10; &lt;div class="flex"&gt;&#10; &lt;div&#10; class="bg-primary-500 dark:bg-primary-300 text-neutral-50 dark:text-neutral-700 min-w-[30px] h-8 text-2xl flex items-center justify-center rounded-full ltr:-ml-12 rtl:-mr-[79px] mt-5"&gt;&#10; &lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"&gt;&#10;&lt;path fill="currentColor" d="M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"/&gt;&lt;/svg&gt;&lt;/span&gt;&#10; &lt;/div&gt;&#10; &lt;div class="block p-6 rounded-lg shadow-2xl flex-1 ms-6 mb-10 break-words"&gt;&#10; &lt;div class="flex justify-between"&gt;&#10; &#10; &lt;h2 class="mt-0"&gt;Bug one found&lt;/h2&gt;&#10; &#10; &#10; &lt;h3 class=""&gt;&#10; &lt;span class="flex cursor-pointer"&gt;&#10; &#10; &#10; &#10; &#10; &lt;span&#10; class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400"&gt;&#10; &#10; day 1&#10; &lt;/span&gt;&#10;&lt;/span&gt;&#10;&#10; &lt;/h3&gt;&#10; &#10; &lt;/div&gt;&#10; &#10; &lt;div class="mb-6"&gt;&#10; The statusline delegate reads &lt;code&gt;.context_window.percentUsed&lt;/code&gt;. Claude Code emits &lt;code&gt;.context_window.used_percentage&lt;/code&gt;. The extraction silently yields nothing, every turn.&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/li&gt;&#10;&#10;&#10;&#10;&#10;&#10;&lt;li&gt;&#10; &lt;div class="flex"&gt;&#10; &lt;div&#10; class="bg-primary-500 dark:bg-primary-300 text-neutral-50 dark:text-neutral-700 min-w-[30px] h-8 text-2xl flex items-center justify-center rounded-full ltr:-ml-12 rtl:-mr-[79px] mt-5"&gt;&#10; &lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"&gt;&#10;&lt;path fill="currentColor" d="M392.8 1.2c-17-4.9-34.7 5-39.6 22l-128 448c-4.9 17 5 34.7 22 39.6s34.7-5 39.6-22l128-448c4.9-17-5-34.7-22-39.6zm80.6 120.1c-12.5 12.5-12.5 32.8 0 45.3L562.7 256l-89.4 89.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0l112-112c12.5-12.5 12.5-32.8 0-45.3l-112-112c-12.5-12.5-32.8-12.5-45.3 0zm-306.7 0c-12.5-12.5-32.8-12.5-45.3 0l-112 112c-12.5 12.5-12.5 32.8 0 45.3l112 112c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L77.3 256l89.4-89.4c12.5-12.5 12.5-32.8 0-45.3z"/&gt;&lt;/svg&gt;&lt;/span&gt;&#10; &lt;/div&gt;&#10; &lt;div class="block p-6 rounded-lg shadow-2xl flex-1 ms-6 mb-10 break-words"&gt;&#10; &lt;div class="flex justify-between"&gt;&#10; &#10; &lt;h2 class="mt-0"&gt;Fixed, and wrong&lt;/h2&gt;&#10; &#10; &#10; &lt;h3 class=""&gt;&#10; &lt;span class="flex cursor-pointer"&gt;&#10; &#10; &#10; &#10; &#10; &lt;span&#10; class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400"&gt;&#10; &#10; day 2&#10; &lt;/span&gt;&#10;&lt;/span&gt;&#10;&#10; &lt;/h3&gt;&#10; &#10; &lt;/div&gt;&#10; &#10; &lt;div class="mb-6"&gt;&#10; Agents now report a correct percentage every turn. The ring still reads 10%.&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/li&gt;&#10;&#10;&#10;&#10;&#10;&#10;&lt;li&gt;&#10; &lt;div class="flex"&gt;&#10; &lt;div&#10; class="bg-primary-500 dark:bg-primary-300 text-neutral-50 dark:text-neutral-700 min-w-[30px] h-8 text-2xl flex items-center justify-center rounded-full ltr:-ml-12 rtl:-mr-[79px] mt-5"&gt;&#10; &lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"&gt;&lt;path fill="currentColor" d="M416 208c0 45.9-14.9 88.3-40 122.7L502.6 457.4c12.5 12.5 12.5 32.8 0 45.3s-32.8 12.5-45.3 0L330.7 376c-34.4 25.2-76.8 40-122.7 40C93.1 416 0 322.9 0 208S93.1 0 208 0S416 93.1 416 208zM208 352a144 144 0 1 0 0-288 144 144 0 1 0 0 288z"/&gt;&lt;/svg&gt;&#10;&lt;/span&gt;&#10; &lt;/div&gt;&#10; &lt;div class="block p-6 rounded-lg shadow-2xl flex-1 ms-6 mb-10 break-words"&gt;&#10; &lt;div class="flex justify-between"&gt;&#10; &#10; &lt;h2 class="mt-0"&gt;Three live probes&lt;/h2&gt;&#10; &#10; &#10; &lt;h3 class=""&gt;&#10; &lt;span class="flex cursor-pointer"&gt;&#10; &#10; &#10; &#10; &#10; &lt;span&#10; class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400"&gt;&#10; &#10; day 2&#10; &lt;/span&gt;&#10;&lt;/span&gt;&#10;&#10; &lt;/h3&gt;&#10; &#10; &lt;/div&gt;&#10; &#10; &lt;div class="mb-6"&gt;&#10; &lt;code&gt;pane.report_metadata&lt;/code&gt; emits nothing. &lt;code&gt;pane.agent_status_changed&lt;/code&gt; carries no tokens. Tokens are assigned in exactly one place.&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/li&gt;&#10;&#10;&#10;&#10;&#10;&#10;&lt;li&gt;&#10; &lt;div class="flex"&gt;&#10; &lt;div&#10; class="bg-primary-500 dark:bg-primary-300 text-neutral-50 dark:text-neutral-700 min-w-[30px] h-8 text-2xl flex items-center justify-center rounded-full ltr:-ml-12 rtl:-mr-[79px] mt-5"&gt;&#10; &lt;span class="relative block icon"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"&gt;&lt;path fill="currentColor" d="M172.5 131.1C228.1 75.51 320.5 75.51 376.1 131.1C426.1 181.1 433.5 260.8 392.4 318.3L391.3 319.9C381 334.2 361 337.6 346.7 327.3C332.3 317 328.9 297 339.2 282.7L340.3 281.1C363.2 249 359.6 205.1 331.7 177.2C300.3 145.8 249.2 145.8 217.7 177.2L105.5 289.5C73.99 320.1 73.99 372 105.5 403.5C133.3 431.4 177.3 435 209.3 412.1L210.9 410.1C225.3 400.7 245.3 404 255.5 418.4C265.8 432.8 262.5 452.8 248.1 463.1L246.5 464.2C188.1 505.3 110.2 498.7 60.21 448.8C3.741 392.3 3.741 300.7 60.21 244.3L172.5 131.1zM467.5 380C411 436.5 319.5 436.5 263 380C213 330 206.5 251.2 247.6 193.7L248.7 192.1C258.1 177.8 278.1 174.4 293.3 184.7C307.7 194.1 311.1 214.1 300.8 229.3L299.7 230.9C276.8 262.1 280.4 306.9 308.3 334.8C339.7 366.2 390.8 366.2 422.3 334.8L534.5 222.5C566 191 566 139.1 534.5 108.5C506.7 80.63 462.7 76.99 430.7 99.9L429.1 101C414.7 111.3 394.7 107.1 384.5 93.58C374.2 79.2 377.5 59.21 391.9 48.94L393.5 47.82C451 6.731 529.8 13.25 579.8 63.24C636.3 119.7 636.3 211.3 579.8 267.7L467.5 380z"/&gt;&lt;/svg&gt;&#10;&lt;/span&gt;&#10; &lt;/div&gt;&#10; &lt;div class="block p-6 rounded-lg shadow-2xl flex-1 ms-6 mb-10 break-words"&gt;&#10; &lt;div class="flex justify-between"&gt;&#10; &#10; &lt;h2 class="mt-0"&gt;Root cause&lt;/h2&gt;&#10; &#10; &#10; &lt;h3 class=""&gt;&#10; &lt;span class="flex cursor-pointer"&gt;&#10; &#10; &#10; &#10; &#10; &lt;span&#10; class="rounded-md border border-primary-400 px-1 py-[1px] text-xs font-normal text-primary-700 dark:border-primary-600 dark:text-primary-400"&gt;&#10; &#10; day 2&#10; &lt;/span&gt;&#10;&lt;/span&gt;&#10;&#10; &lt;/h3&gt;&#10; &#10; &lt;/div&gt;&#10; &#10; &lt;div class="mb-6"&gt;&#10; The daemon snapshotted once on connect. Every ring froze at whatever it read at that instant.&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10; &lt;/div&gt;&#10;&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&#10;&#10;&lt;h2 class="relative group"&gt;Bug one: a field name that does not exist&#10; &lt;div id="bug-one-a-field-name-that-does-not-exist" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#bug-one-a-field-name-that-does-not-exist" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;HerdDeck ships a statusline delegate. It sits in front of whatever statusline you already have, forwards the per-turn JSON payload on to herdr, and passes your original output through untouched. It read this:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;.context_window.percentUsed&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Claude Code emits &lt;code&gt;.context_window.used_percentage&lt;/code&gt;.&lt;/p&gt;&#10;&lt;p&gt;Getting the field name wrong is unremarkable. How it failed is the part worth keeping. A statusline script runs on every turn of every session, and a statusline script that throws takes your prompt down with it, so the whole thing is written to fall through silently on every path. Wrong field name, malformed JSON, &lt;code&gt;jq&lt;/code&gt; missing, all of it produces the same result: no error, no output, no report, forever.&lt;/p&gt;&#10;&lt;p&gt;Silent-by-design is correct for a statusline. It is also a category of bug where nothing anywhere tells you the value never arrived, and the only symptom is a number somewhere else that stops changing.&lt;/p&gt;&#10;&lt;p&gt;There is a small joke at my expense here. I wrote &lt;a href="https://nick-liu.com/posts/statusline-side-channel/" &gt;a post about this exact payload&lt;/a&gt; in May, and that post prints the schema with the correct field name in it. My own published documentation had it right. The code I shipped three months later did not.&lt;/p&gt;&#10;&lt;p&gt;The fix keeps the wrong name deliberately, which was not my first instinct:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# Field name: Claude Code emits `.context_window.used_percentage`. An&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# earlier revision of this script read `.percentUsed`, which does not&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# exist — the extraction silently yielded nothing and the donut never&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# lit up. `percentUsed` is kept only as a fallback so the script also&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# works against any build that ever used it; `used_percentage` wins.&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;PCT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;%s&amp;#39;&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$INPUT&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; jq -r &lt;span class="s1"&gt;&amp;#39;.context_window | (.used_percentage // .percentUsed // empty)&amp;#39;&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="k"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Two tests pin the behaviour: one that the fallback works, one that &lt;code&gt;used_percentage&lt;/code&gt; wins when both are present. A silent fallback needs a test asserting the &lt;em&gt;ordering&lt;/em&gt;, or it is just a second way to be quietly wrong.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The fix worked and the ring did not move&#10; &lt;div id="the-fix-worked-and-the-ring-did-not-move" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-fix-worked-and-the-ring-did-not-move" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;This is the part of the debugging that went wrong, and it is why the post exists.&lt;/p&gt;&#10;&lt;p&gt;After the field-name fix, every agent reported a correct, changing percentage on every turn. I could watch the numbers arrive. And the ring still sat at 10%.&lt;/p&gt;&#10;&lt;p&gt;For an hour or so I was debugging the wrong thing, because the evidence pointed backwards. A fix that produces correct data and no visible change reads as &amp;ldquo;the fix did not work&amp;rdquo;. I went back over the delegate. I checked the JSON. I checked that the daemon was receiving it. All fine, all pointless, because the second bug had been there the entire time and the first one had been hiding it perfectly.&lt;/p&gt;&#10;&lt;p&gt;A cosmetic symptom with two independent causes is much harder than a symptom with one hard failure. With one cause, fixing it resolves the symptom and you are done. With two, fixing the first one changes nothing observable, and the natural inference is that you were wrong about the first one.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Three probes against a live server&#10; &lt;div id="three-probes-against-a-live-server" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#three-probes-against-a-live-server" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;So I stopped reading code and started asking the server questions. Three probes, against herdr 0.8.0, with a real agent working in a real pane.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Probe&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Result&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Report metadata to a pane while subscribed to &lt;code&gt;pane.updated&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;zero events&lt;/strong&gt; for that pane. &lt;code&gt;pane.report_metadata&lt;/code&gt; emits nothing at all&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Inspect &lt;code&gt;pane.agent_status_changed&lt;/code&gt; payload&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;carries only &lt;code&gt;{pane_id, agent_status}&lt;/code&gt;. No tokens&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Grep the cache for token assignment&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;exactly one site: the &lt;code&gt;session.snapshot&lt;/code&gt; seed&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Put those together and the answer is unavoidable. The daemon snapshotted once on connect, and every ring froze at whatever the snapshot happened to contain at that instant. Nothing in the protocol was ever going to push a token count at it.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;&lt;code&gt;pane.updated&lt;/code&gt; looked like the answer&#10; &lt;div id="paneupdated-looked-like-the-answer" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#paneupdated-looked-like-the-answer" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;There is an event called &lt;code&gt;pane.updated&lt;/code&gt; and it does carry tokens. It looked like the fix for about ten minutes.&lt;/p&gt;&#10;&lt;p&gt;It is not, for two reasons, and the second one is decisive.&lt;/p&gt;&#10;&lt;p&gt;It is chatty. It fires on scroll offsets and status flicker, and the measurement recorded in the source is roughly 25 events in 2.5 seconds from a single active pane. Subscribing to that to catch a number that changes once per turn is a poor trade.&lt;/p&gt;&#10;&lt;p&gt;And it does not fire for a metadata report at all. That is the case that matters. A pane whose context moved while it sat idle would simply never be announced, which is precisely the situation where you want the ring to be honest, because you are looking at a key rather than at the terminal.&lt;/p&gt;&#10;&lt;p&gt;Rejecting an event on measurement rather than on taste is worth doing explicitly. &lt;code&gt;pane.updated&lt;/code&gt; is the obvious answer, it is in the schema, and it would have shipped a ring that worked most of the time and lied when you were not watching.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Re-read, merge, do not re-seed&#10; &lt;div id="re-read-merge-do-not-re-seed" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#re-read-merge-do-not-re-seed" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;div class="not-prose diagram diagram-light"&gt;&lt;svg id="df72daf3532c27fe2light" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 722.1484375px;" viewBox="0 0 722.1484375 430" role="graphics-document document" aria-roledescription="flowchart-v2"&gt;&lt;style&gt;#df72daf3532c27fe2light{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#df72daf3532c27fe2light .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#df72daf3532c27fe2light .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#df72daf3532c27fe2light .error-icon{fill:rgb(255, 255, 255);}#df72daf3532c27fe2light .error-text{fill:#000000;stroke:#000000;}#df72daf3532c27fe2light .edge-thickness-normal{stroke-width:1px;}#df72daf3532c27fe2light .edge-thickness-thick{stroke-width:3.5px;}#df72daf3532c27fe2light .edge-pattern-solid{stroke-dasharray:0;}#df72daf3532c27fe2light .edge-thickness-invisible{stroke-width:0;fill:none;}#df72daf3532c27fe2light .edge-pattern-dashed{stroke-dasharray:3;}#df72daf3532c27fe2light .edge-pattern-dotted{stroke-dasharray:2;}#df72daf3532c27fe2light .marker{fill:rgb(74, 86, 92);stroke:rgb(74, 86, 92);}#df72daf3532c27fe2light .marker.cross{stroke:rgb(74, 86, 92);}#df72daf3532c27fe2light svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#df72daf3532c27fe2light p{margin:0;}#df72daf3532c27fe2light .label{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;color:#333;}#df72daf3532c27fe2light .cluster-label text{fill:#000000;}#df72daf3532c27fe2light .cluster-label span{color:#000000;}#df72daf3532c27fe2light .cluster-label span p{background-color:transparent;}#df72daf3532c27fe2light .label text,#df72daf3532c27fe2light span{fill:#333;color:#333;}#df72daf3532c27fe2light .node rect,#df72daf3532c27fe2light .node circle,#df72daf3532c27fe2light .node ellipse,#df72daf3532c27fe2light .node polygon,#df72daf3532c27fe2light .node path{fill:rgb(255, 255, 255);stroke:rgb(204, 216, 222);stroke-width:1px;}#df72daf3532c27fe2light .rough-node .label text,#df72daf3532c27fe2light .node .label text,#df72daf3532c27fe2light .image-shape .label,#df72daf3532c27fe2light .icon-shape .label{text-anchor:middle;}#df72daf3532c27fe2light .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#df72daf3532c27fe2light .rough-node .label,#df72daf3532c27fe2light .node .label,#df72daf3532c27fe2light .image-shape .label,#df72daf3532c27fe2light .icon-shape .label{text-align:center;}#df72daf3532c27fe2light .node.clickable{cursor:pointer;}#df72daf3532c27fe2light .root .anchor path{fill:rgb(74, 86, 92)!important;stroke-width:0;stroke:rgb(74, 86, 92);}#df72daf3532c27fe2light .arrowheadPath{fill:#000000;}#df72daf3532c27fe2light .edgePath .path{stroke:rgb(74, 86, 92);stroke-width:1px;}#df72daf3532c27fe2light .flowchart-link{stroke:rgb(74, 86, 92);fill:none;}#df72daf3532c27fe2light .edgeLabel{background-color:rgb(182, 240, 255);text-align:center;}#df72daf3532c27fe2light .edgeLabel p{background-color:rgb(182, 240, 255);}#df72daf3532c27fe2light .edgeLabel rect{opacity:0.5;background-color:rgb(182, 240, 255);fill:rgb(182, 240, 255);}#df72daf3532c27fe2light .labelBkg{background-color:rgba(182, 240, 255, 0.5);}#df72daf3532c27fe2light .cluster rect{fill:rgb(255, 255, 255);stroke:rgb(129, 146, 154);stroke-width:1px;}#df72daf3532c27fe2light .cluster text{fill:#000000;}#df72daf3532c27fe2light .cluster span{color:#000000;}#df72daf3532c27fe2light div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:12px;background:rgb(255, 255, 255);border:1px solid rgb(129, 146, 154);border-radius:2px;pointer-events:none;z-index:100;}#df72daf3532c27fe2light .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#df72daf3532c27fe2light rect.text{fill:none;stroke-width:0;}#df72daf3532c27fe2light .icon-shape,#df72daf3532c27fe2light .image-shape{background-color:rgb(182, 240, 255);text-align:center;}#df72daf3532c27fe2light .icon-shape p,#df72daf3532c27fe2light .image-shape p{background-color:rgb(182, 240, 255);padding:2px;}#df72daf3532c27fe2light .icon-shape .label rect,#df72daf3532c27fe2light .image-shape .label rect{opacity:0.5;background-color:rgb(182, 240, 255);fill:rgb(182, 240, 255);}#df72daf3532c27fe2light .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#df72daf3532c27fe2light .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#df72daf3532c27fe2light .node .neo-node{stroke:rgb(204, 216, 222);}#df72daf3532c27fe2light [data-look="neo"].node rect,#df72daf3532c27fe2light [data-look="neo"].cluster rect,#df72daf3532c27fe2light [data-look="neo"].node polygon{stroke:url(#df72daf3532c27fe2light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2light [data-look="neo"].swimlane.cluster rect{filter:none;}#df72daf3532c27fe2light [data-look="neo"].node path{stroke:url(#df72daf3532c27fe2light-gradient);stroke-width:1px;}#df72daf3532c27fe2light [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2light [data-look="neo"].node .neo-line path{stroke:rgb(204, 216, 222);filter:none;}#df72daf3532c27fe2light [data-look="neo"].node circle{stroke:url(#df72daf3532c27fe2light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2light [data-look="neo"].node circle .state-start{fill:#000000;}#df72daf3532c27fe2light [data-look="neo"].icon-shape .icon{fill:url(#df72daf3532c27fe2light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2light [data-look="neo"].icon-shape .icon-neo path{stroke:url(#df72daf3532c27fe2light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2light :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"&gt;&lt;path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"&gt;&lt;polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2light_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class="root"&gt;&lt;g class="clusters"&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M100.984,178L100.984,186.167C100.984,194.333,100.984,210.667,100.984,224.333C100.984,238,100.984,249,100.984,254.5L100.984,260" id="df72daf3532c27fe2light-L_C_S_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_S_0" data-points="W3sieCI6MTAwLjk4NDM3NSwieSI6MTc4fSx7IngiOjEwMC45ODQzNzUsInkiOjIyN30seyJ4IjoxMDAuOTg0Mzc1LCJ5IjoyNjR9XQ==" data-look="classic" marker-end="url(#df72daf3532c27fe2light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M100.984,318L100.984,322.167C100.984,326.333,100.984,334.667,127.663,344.322C154.342,353.977,207.701,364.954,234.38,370.443L261.059,375.931" id="df72daf3532c27fe2light-L_S_T1_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_S_T1_0" data-points="W3sieCI6MTAwLjk4NDM3NSwieSI6MzE4fSx7IngiOjEwMC45ODQzNzUsInkiOjM0M30seyJ4IjoyNjQuOTc2NTYyNSwieSI6Mzc2LjczNzE1NzY5MzAyMDk1fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M314.547,190L314.547,196.167C314.547,202.333,314.547,214.667,317.811,226.424C321.075,238.182,327.603,249.364,330.867,254.955L334.131,260.546" id="df72daf3532c27fe2light-L_E_ST_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_E_ST_0" data-points="W3sieCI6MzE0LjU0Njg3NSwieSI6MTkwfSx7IngiOjMxNC41NDY4NzUsInkiOjIyN30seyJ4IjozMzYuMTQ3NTIxOTcyNjU2MjUsInkiOjI2NH1d" data-look="classic" marker-end="url(#df72daf3532c27fe2light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M580.121,62L580.121,66.167C580.121,70.333,580.121,78.667,580.121,88.333C580.121,98,580.121,109,580.121,114.5L580.121,120" id="df72daf3532c27fe2light-L_T_S2_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_T_S2_0" data-points="W3sieCI6NTgwLjEyMTA5Mzc1LCJ5Ijo2Mn0seyJ4Ijo1ODAuMTIxMDkzNzUsInkiOjg3fSx7IngiOjU4MC4xMjEwOTM3NSwieSI6MTI0fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M593.395,178L597.41,186.167C601.425,194.333,609.455,210.667,613.469,224.333C617.484,238,617.484,249,617.484,254.5L617.484,260" id="df72daf3532c27fe2light-L_S2_M_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_S2_M_0" data-points="W3sieCI6NTkzLjM5NDg5MTAzNjE4NDIsInkiOjE3OH0seyJ4Ijo2MTcuNDg0Mzc1LCJ5IjoyMjd9LHsieCI6NjE3LjQ4NDM3NSwieSI6MjY0fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M617.484,318L617.484,322.167C617.484,326.333,617.484,334.667,588.978,344.454C560.472,354.241,503.46,365.482,474.954,371.102L446.448,376.723" id="df72daf3532c27fe2light-L_M_T1_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_M_T1_0" data-points="W3sieCI6NjE3LjQ4NDM3NSwieSI6MzE4fSx7IngiOjYxNy40ODQzNzUsInkiOjM0M30seyJ4Ijo0NDIuNTIzNDM3NSwieSI6Mzc3LjQ5NjcxMTg5MDUxNDg2fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M541.268,178L529.517,186.167C517.765,194.333,494.261,210.667,471.645,224.684C449.029,238.701,427.3,250.402,416.435,256.253L405.571,262.103" id="df72daf3532c27fe2light-L_S2_ST_0" class="edge-thickness-normal edge-pattern-dotted edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_S2_ST_0" data-points="W3sieCI6NTQxLjI2ODM0OTA5NTM5NDgsInkiOjE3OH0seyJ4Ijo0NzAuNzU3ODEyNSwieSI6MjI3fSx7IngiOjQwMi4wNDkwMTEyMzA0Njg3NSwieSI6MjY0fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_C_S_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_S_T1_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_E_ST_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_T_S2_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_S2_M_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_M_T1_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel" transform="translate(473.97144, 224.76675)"&gt;&lt;g class="label" data-id="L_S2_ST_0" transform="translate(-54.7265625, -12)"&gt;&lt;foreignObject width="109.453125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;p&gt;never re-seeds&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="df72daf3532c27fe2light-flowchart-C-0" data-look="classic" transform="translate(100.984375, 151)"&gt;&lt;rect class="basic label-container" style="" x="-59.4765625" y="-27" width="118.953125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-29.4765625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="58.953125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;connect&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2light-flowchart-S-1" data-look="classic" transform="translate(100.984375, 291)"&gt;&lt;rect class="basic label-container" style="" x="-92.984375" y="-27" width="185.96875" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-62.984375, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="125.96875" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;session.snapshot&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2light-flowchart-T1-3" data-look="classic" transform="translate(353.75, 395)"&gt;&lt;rect class="basic label-container" style="" x="-88.7734375" y="-27" width="177.546875" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-58.7734375, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="117.546875" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;tokens assigned&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2light-flowchart-E-4" data-look="classic" transform="translate(314.546875, 151)"&gt;&lt;rect class="basic label-container" style="" x="-102.6015625" y="-39" width="205.203125" height="78"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-72.6015625, -24)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="145.203125" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;pushed events&lt;br&gt;pane_closed, status&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2light-flowchart-ST-5" data-look="classic" transform="translate(351.91015625, 291)"&gt;&lt;rect class="basic label-container" style="" x="-72.0625" y="-27" width="144.125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-42.0625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="84.125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;cache state&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2light-flowchart-T-6" data-look="classic" transform="translate(580.12109375, 35)"&gt;&lt;rect class="basic label-container" style="" x="-63.2421875" y="-27" width="126.484375" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-33.2421875, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="66.484375" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;10s timer&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2light-flowchart-S2-7" data-look="classic" transform="translate(580.12109375, 151)"&gt;&lt;rect class="basic label-container" style="" x="-85.09375" y="-27" width="170.1875" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-55.09375, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="110.1875" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;snapshot again&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2light-flowchart-M-9" data-look="classic" transform="translate(617.484375, 291)"&gt;&lt;rect class="basic label-container" style="" x="-96.6640625" y="-27" width="193.328125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-66.6640625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="133.328125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;merge tokens only&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id="df72daf3532c27fe2light-drop-shadow" height="130%" width="130%"&gt;&lt;feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id="df72daf3532c27fe2light-drop-shadow-small" height="150%" width="150%"&gt;&lt;feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;linearGradient id="df72daf3532c27fe2light-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"&gt;&lt;stop offset="0%" stop-color="rgb(204, 216, 222)" stop-opacity="1"&gt;&lt;/stop&gt;&lt;stop offset="100%" stop-color="rgb(28, 209, 255)" stop-opacity="1"&gt;&lt;/stop&gt;&lt;/linearGradient&gt;&lt;/svg&gt;&lt;/div&gt;&#10; &lt;div class="not-prose diagram diagram-dark"&gt;&lt;svg id="df72daf3532c27fe2dark" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 722.1484375px;" viewBox="0 0 722.1484375 430" role="graphics-document document" aria-roledescription="flowchart-v2"&gt;&lt;style&gt;#df72daf3532c27fe2dark{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#ccc;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#df72daf3532c27fe2dark .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#df72daf3532c27fe2dark .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#df72daf3532c27fe2dark .error-icon{fill:#a44141;}#df72daf3532c27fe2dark .error-text{fill:#ddd;stroke:#ddd;}#df72daf3532c27fe2dark .edge-thickness-normal{stroke-width:1px;}#df72daf3532c27fe2dark .edge-thickness-thick{stroke-width:3.5px;}#df72daf3532c27fe2dark .edge-pattern-solid{stroke-dasharray:0;}#df72daf3532c27fe2dark .edge-thickness-invisible{stroke-width:0;fill:none;}#df72daf3532c27fe2dark .edge-pattern-dashed{stroke-dasharray:3;}#df72daf3532c27fe2dark .edge-pattern-dotted{stroke-dasharray:2;}#df72daf3532c27fe2dark .marker{fill:lightgrey;stroke:lightgrey;}#df72daf3532c27fe2dark .marker.cross{stroke:lightgrey;}#df72daf3532c27fe2dark svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#df72daf3532c27fe2dark p{margin:0;}#df72daf3532c27fe2dark .label{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;color:#ccc;}#df72daf3532c27fe2dark .cluster-label text{fill:#F9FFFE;}#df72daf3532c27fe2dark .cluster-label span{color:#F9FFFE;}#df72daf3532c27fe2dark .cluster-label span p{background-color:transparent;}#df72daf3532c27fe2dark .label text,#df72daf3532c27fe2dark span{fill:#ccc;color:#ccc;}#df72daf3532c27fe2dark .node rect,#df72daf3532c27fe2dark .node circle,#df72daf3532c27fe2dark .node ellipse,#df72daf3532c27fe2dark .node polygon,#df72daf3532c27fe2dark .node path{fill:#1f2020;stroke:#ccc;stroke-width:1px;}#df72daf3532c27fe2dark .rough-node .label text,#df72daf3532c27fe2dark .node .label text,#df72daf3532c27fe2dark .image-shape .label,#df72daf3532c27fe2dark .icon-shape .label{text-anchor:middle;}#df72daf3532c27fe2dark .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#df72daf3532c27fe2dark .rough-node .label,#df72daf3532c27fe2dark .node .label,#df72daf3532c27fe2dark .image-shape .label,#df72daf3532c27fe2dark .icon-shape .label{text-align:center;}#df72daf3532c27fe2dark .node.clickable{cursor:pointer;}#df72daf3532c27fe2dark .root .anchor path{fill:lightgrey!important;stroke-width:0;stroke:lightgrey;}#df72daf3532c27fe2dark .arrowheadPath{fill:lightgrey;}#df72daf3532c27fe2dark .edgePath .path{stroke:lightgrey;stroke-width:1px;}#df72daf3532c27fe2dark .flowchart-link{stroke:lightgrey;fill:none;}#df72daf3532c27fe2dark .edgeLabel{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#df72daf3532c27fe2dark .edgeLabel p{background-color:hsl(0, 0%, 34.4117647059%);}#df72daf3532c27fe2dark .edgeLabel rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#df72daf3532c27fe2dark .labelBkg{background-color:rgba(87.75, 87.75, 87.75, 0.5);}#df72daf3532c27fe2dark .cluster rect{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:rgba(255, 255, 255, 0.25);stroke-width:1px;}#df72daf3532c27fe2dark .cluster text{fill:#F9FFFE;}#df72daf3532c27fe2dark .cluster span{color:#F9FFFE;}#df72daf3532c27fe2dark div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:12px;background:hsl(20, 1.5873015873%, 12.3529411765%);border:1px solid rgba(255, 255, 255, 0.25);border-radius:2px;pointer-events:none;z-index:100;}#df72daf3532c27fe2dark .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#ccc;}#df72daf3532c27fe2dark rect.text{fill:none;stroke-width:0;}#df72daf3532c27fe2dark .icon-shape,#df72daf3532c27fe2dark .image-shape{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#df72daf3532c27fe2dark .icon-shape p,#df72daf3532c27fe2dark .image-shape p{background-color:hsl(0, 0%, 34.4117647059%);padding:2px;}#df72daf3532c27fe2dark .icon-shape .label rect,#df72daf3532c27fe2dark .image-shape .label rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#df72daf3532c27fe2dark .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#df72daf3532c27fe2dark .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#df72daf3532c27fe2dark .node .neo-node{stroke:#ccc;}#df72daf3532c27fe2dark [data-look="neo"].node rect,#df72daf3532c27fe2dark [data-look="neo"].cluster rect,#df72daf3532c27fe2dark [data-look="neo"].node polygon{stroke:url(#df72daf3532c27fe2dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2dark [data-look="neo"].swimlane.cluster rect{filter:none;}#df72daf3532c27fe2dark [data-look="neo"].node path{stroke:url(#df72daf3532c27fe2dark-gradient);stroke-width:1px;}#df72daf3532c27fe2dark [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2dark [data-look="neo"].node .neo-line path{stroke:#ccc;filter:none;}#df72daf3532c27fe2dark [data-look="neo"].node circle{stroke:url(#df72daf3532c27fe2dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2dark [data-look="neo"].node circle .state-start{fill:#000000;}#df72daf3532c27fe2dark [data-look="neo"].icon-shape .icon{fill:url(#df72daf3532c27fe2dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2dark [data-look="neo"].icon-shape .icon-neo path{stroke:url(#df72daf3532c27fe2dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#df72daf3532c27fe2dark :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"&gt;&lt;path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"&gt;&lt;polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="df72daf3532c27fe2dark_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class="root"&gt;&lt;g class="clusters"&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M100.984,178L100.984,186.167C100.984,194.333,100.984,210.667,100.984,224.333C100.984,238,100.984,249,100.984,254.5L100.984,260" id="df72daf3532c27fe2dark-L_C_S_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_C_S_0" data-points="W3sieCI6MTAwLjk4NDM3NSwieSI6MTc4fSx7IngiOjEwMC45ODQzNzUsInkiOjIyN30seyJ4IjoxMDAuOTg0Mzc1LCJ5IjoyNjR9XQ==" data-look="classic" marker-end="url(#df72daf3532c27fe2dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M100.984,318L100.984,322.167C100.984,326.333,100.984,334.667,127.663,344.322C154.342,353.977,207.701,364.954,234.38,370.443L261.059,375.931" id="df72daf3532c27fe2dark-L_S_T1_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_S_T1_0" data-points="W3sieCI6MTAwLjk4NDM3NSwieSI6MzE4fSx7IngiOjEwMC45ODQzNzUsInkiOjM0M30seyJ4IjoyNjQuOTc2NTYyNSwieSI6Mzc2LjczNzE1NzY5MzAyMDk1fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M314.547,190L314.547,196.167C314.547,202.333,314.547,214.667,317.811,226.424C321.075,238.182,327.603,249.364,330.867,254.955L334.131,260.546" id="df72daf3532c27fe2dark-L_E_ST_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_E_ST_0" data-points="W3sieCI6MzE0LjU0Njg3NSwieSI6MTkwfSx7IngiOjMxNC41NDY4NzUsInkiOjIyN30seyJ4IjozMzYuMTQ3NTIxOTcyNjU2MjUsInkiOjI2NH1d" data-look="classic" marker-end="url(#df72daf3532c27fe2dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M580.121,62L580.121,66.167C580.121,70.333,580.121,78.667,580.121,88.333C580.121,98,580.121,109,580.121,114.5L580.121,120" id="df72daf3532c27fe2dark-L_T_S2_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_T_S2_0" data-points="W3sieCI6NTgwLjEyMTA5Mzc1LCJ5Ijo2Mn0seyJ4Ijo1ODAuMTIxMDkzNzUsInkiOjg3fSx7IngiOjU4MC4xMjEwOTM3NSwieSI6MTI0fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M593.395,178L597.41,186.167C601.425,194.333,609.455,210.667,613.469,224.333C617.484,238,617.484,249,617.484,254.5L617.484,260" id="df72daf3532c27fe2dark-L_S2_M_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_S2_M_0" data-points="W3sieCI6NTkzLjM5NDg5MTAzNjE4NDIsInkiOjE3OH0seyJ4Ijo2MTcuNDg0Mzc1LCJ5IjoyMjd9LHsieCI6NjE3LjQ4NDM3NSwieSI6MjY0fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M617.484,318L617.484,322.167C617.484,326.333,617.484,334.667,588.978,344.454C560.472,354.241,503.46,365.482,474.954,371.102L446.448,376.723" id="df72daf3532c27fe2dark-L_M_T1_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_M_T1_0" data-points="W3sieCI6NjE3LjQ4NDM3NSwieSI6MzE4fSx7IngiOjYxNy40ODQzNzUsInkiOjM0M30seyJ4Ijo0NDIuNTIzNDM3NSwieSI6Mzc3LjQ5NjcxMTg5MDUxNDg2fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M541.268,178L529.517,186.167C517.765,194.333,494.261,210.667,471.645,224.684C449.029,238.701,427.3,250.402,416.435,256.253L405.571,262.103" id="df72daf3532c27fe2dark-L_S2_ST_0" class="edge-thickness-normal edge-pattern-dotted edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_S2_ST_0" data-points="W3sieCI6NTQxLjI2ODM0OTA5NTM5NDgsInkiOjE3OH0seyJ4Ijo0NzAuNzU3ODEyNSwieSI6MjI3fSx7IngiOjQwMi4wNDkwMTEyMzA0Njg3NSwieSI6MjY0fV0=" data-look="classic" marker-end="url(#df72daf3532c27fe2dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_C_S_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_S_T1_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_E_ST_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_T_S2_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_S2_M_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_M_T1_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel" transform="translate(473.97144, 224.76675)"&gt;&lt;g class="label" data-id="L_S2_ST_0" transform="translate(-54.7265625, -12)"&gt;&lt;foreignObject width="109.453125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;p&gt;never re-seeds&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="df72daf3532c27fe2dark-flowchart-C-0" data-look="classic" transform="translate(100.984375, 151)"&gt;&lt;rect class="basic label-container" style="" x="-59.4765625" y="-27" width="118.953125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-29.4765625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="58.953125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;connect&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2dark-flowchart-S-1" data-look="classic" transform="translate(100.984375, 291)"&gt;&lt;rect class="basic label-container" style="" x="-92.984375" y="-27" width="185.96875" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-62.984375, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="125.96875" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;session.snapshot&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2dark-flowchart-T1-3" data-look="classic" transform="translate(353.75, 395)"&gt;&lt;rect class="basic label-container" style="" x="-88.7734375" y="-27" width="177.546875" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-58.7734375, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="117.546875" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;tokens assigned&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2dark-flowchart-E-4" data-look="classic" transform="translate(314.546875, 151)"&gt;&lt;rect class="basic label-container" style="" x="-102.6015625" y="-39" width="205.203125" height="78"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-72.6015625, -24)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="145.203125" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;pushed events&lt;br&gt;pane_closed, status&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2dark-flowchart-ST-5" data-look="classic" transform="translate(351.91015625, 291)"&gt;&lt;rect class="basic label-container" style="" x="-72.0625" y="-27" width="144.125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-42.0625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="84.125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;cache state&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2dark-flowchart-T-6" data-look="classic" transform="translate(580.12109375, 35)"&gt;&lt;rect class="basic label-container" style="" x="-63.2421875" y="-27" width="126.484375" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-33.2421875, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="66.484375" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;10s timer&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2dark-flowchart-S2-7" data-look="classic" transform="translate(580.12109375, 151)"&gt;&lt;rect class="basic label-container" style="" x="-85.09375" y="-27" width="170.1875" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-55.09375, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="110.1875" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;snapshot again&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="df72daf3532c27fe2dark-flowchart-M-9" data-look="classic" transform="translate(617.484375, 291)"&gt;&lt;rect class="basic label-container" style="" x="-96.6640625" y="-27" width="193.328125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-66.6640625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="133.328125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;merge tokens only&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id="df72daf3532c27fe2dark-drop-shadow" height="130%" width="130%"&gt;&lt;feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#FFFFFF"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id="df72daf3532c27fe2dark-drop-shadow-small" height="150%" width="150%"&gt;&lt;feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#FFFFFF"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;linearGradient id="df72daf3532c27fe2dark-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"&gt;&lt;stop offset="0%" stop-color="#cccccc" stop-opacity="1"&gt;&lt;/stop&gt;&lt;stop offset="100%" stop-color="hsl(180, 0%, 18.3529411765%)" stop-opacity="1"&gt;&lt;/stop&gt;&lt;/linearGradient&gt;&lt;/svg&gt;&lt;/div&gt;&#10;&lt;p&gt;The fix is a periodic snapshot on a 10-second default, and the merge is the load-bearing part:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/** Token re-read period; 0 disables it. Default 10s — roughly&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * twice Claude Code&amp;#39;s default statusline refresh, so the donut&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * trails the real percentage by at most one report. */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;tokensRefreshMs?&lt;/span&gt;: &lt;span class="kt"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// ...&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokensRefreshMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tokensRefreshMs&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;The timer&amp;rsquo;s snapshot can be &lt;em&gt;older&lt;/em&gt; than pushed events the cache has already applied. Re-seeding from it would let a stale read undo a pane close that a newer event had already processed, resurrecting a pane that is gone. There is a test pinning exactly that, and its comment says why:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// The refresh re-reads a full snapshot but must merge tokens ONLY:&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// re-seeding would let a stale snapshot undo a close that a newer&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// pushed event already applied.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;One correction to my own framing, because I described this as &amp;ldquo;tokens only&amp;rdquo; for a while and it stopped being true. The merge now also carries the &lt;code&gt;focused&lt;/code&gt; flag, deliberately and with its own justification comment. If you go looking at that function expecting the name to be the specification, it will not be.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Lessons&#10; &lt;div id="lessons" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lessons" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;A suspiciously round number in a noisy display means the display is not reading anything. Zero says &amp;ldquo;nothing arrived&amp;rdquo;, and a plausible number says &amp;ldquo;this works&amp;rdquo;. Round says &amp;ldquo;orphan&amp;rdquo;.&lt;/li&gt;&#10;&lt;li&gt;Silent-by-design is right for a statusline and it removes your only failure signal. A wrong field name in a script built never to break a prompt produces no error, no output, and no report, on every turn, indefinitely.&lt;/li&gt;&#10;&lt;li&gt;When a correct fix produces no visible change, consider that you fixed one of two bugs before concluding you fixed none. Two independent causes of one cosmetic symptom is the case where evidence points backwards.&lt;/li&gt;&#10;&lt;li&gt;Probe the live server instead of reading its schema. &amp;ldquo;Does this call emit an event at all&amp;rdquo; is a question a schema cannot answer and a five-minute experiment can.&lt;/li&gt;&#10;&lt;li&gt;Reject the obvious event on measurement, not taste, and write down the measurement. &lt;code&gt;pane.updated&lt;/code&gt; carried the field I wanted and did not fire for the case I needed.&lt;/li&gt;&#10;&lt;li&gt;A periodic re-read must merge, never re-seed. A refresh that is older than the events you have already applied will happily undo them.&lt;/li&gt;&#10;&lt;li&gt;If a fallback exists, test the ordering, not just the fallback. Otherwise it is a second way to be quietly wrong.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;References&#10; &lt;div id="references" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#references" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Claude Code statusline docs, source of truth for the payload schema: &lt;a href="https://code.claude.com/docs/en/statusline" target="_blank" rel="noreferrer"&gt;https://code.claude.com/docs/en/statusline&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The post that printed the correct field name three months before I got it wrong: &lt;a href="https://nick-liu.com/posts/statusline-side-channel/" &gt;The Claude Code statusline is a per-turn telemetry side channel&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The rebuild this ring belongs to: &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" &gt;I deleted six subsystems by swapping one dependency.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;HerdDeck statusline delegate: &lt;a href="https://github.com/nickboy/herddeck/blob/main/scripts/herddeck-statusline.sh" target="_blank" rel="noreferrer"&gt;&lt;code&gt;scripts/herddeck-statusline.sh&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;HerdDeck state cache, including &lt;code&gt;mergeTokensFromSnapshot&lt;/code&gt;: &lt;a href="https://github.com/nickboy/herddeck/blob/main/packages/daemon/src/stateCache.ts" target="_blank" rel="noreferrer"&gt;&lt;code&gt;packages/daemon/src/stateCache.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;HerdDeck target monitor, the refresh timer: &lt;a href="https://github.com/nickboy/herddeck/blob/main/packages/daemon/src/herdr/monitor.ts" target="_blank" rel="noreferrer"&gt;&lt;code&gt;packages/daemon/src/herdr/monitor.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>My backoff logged errors=1 492 times. It was answering the wrong question.</title><link>https://nick-liu.com/posts/herddeck-backoff-wrong-question/</link><pubDate>Mon, 31 Aug 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/herddeck-backoff-wrong-question/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;The daemon's log contained the line `plan poller cadence: errors=1` four hundred and ninety-two times, and `errors=4` five times. A counter that is supposed to climb during an outage had spent its entire life bouncing off one. A third of my requests were being rate-limited and the backoff built to handle that never engaged once, because it was answering a question nobody had asked.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 2 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me."&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question." aria-current="step"&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other."&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%."&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end."&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;herdr&lt;/strong&gt; &lt;code&gt;0.8.0&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;The Plan Usage key on the Stream Deck renders my Claude plan&amp;rsquo;s 5-hour and 7-day windows with a countdown to the next reset. HerdDeck polls Anthropic&amp;rsquo;s OAuth usage endpoint to fill it. That endpoint is undocumented, which is a story &lt;a href="https://nick-liu.com/posts/plan-usage-statusline-pivot/" &gt;I have already told on this blog&lt;/a&gt; and will be correcting later in this post.&lt;/p&gt;</description><content:encoded>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;The daemon's log contained the line `plan poller cadence: errors=1` four hundred and ninety-two times, and `errors=4` five times. A counter that is supposed to climb during an outage had spent its entire life bouncing off one. A third of my requests were being rate-limited and the backoff built to handle that never engaged once, because it was answering a question nobody had asked.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 2 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-done"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me."&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question." aria-current="step"&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other."&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%."&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end."&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;herdr&lt;/strong&gt; &lt;code&gt;0.8.0&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;The Plan Usage key on the Stream Deck renders my Claude plan&amp;rsquo;s 5-hour and 7-day windows with a countdown to the next reset. HerdDeck polls Anthropic&amp;rsquo;s OAuth usage endpoint to fill it. That endpoint is undocumented, which is a story &lt;a href="https://nick-liu.com/posts/plan-usage-statusline-pivot/" &gt;I have already told on this blog&lt;/a&gt; and will be correcting later in this post.&lt;/p&gt;&#10;&lt;p&gt;The log showed this, live, on my machine:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;successes: 1136&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HTTP 429: 543&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;recent ticks: update error update update error update update error update error update update&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Two facts in that block, and the second one is the whole post.&lt;/p&gt;&#10;&lt;p&gt;A third of requests were rate-limited. And the failures arrived &lt;strong&gt;interleaved, not in runs&lt;/strong&gt;.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Consecutive-error backoff answers &amp;ldquo;is the service down&amp;rdquo;&#10; &lt;div id="consecutive-error-backoff-answers-is-the-service-down" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#consecutive-error-backoff-answers-is-the-service-down" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;The mechanism I had was textbook exponential backoff on a consecutive-error counter. Every failure increments it and lengthens the interval. Any success resets it to zero.&lt;/p&gt;&#10;&lt;p&gt;That shape encodes an assumption: failures cluster. It is designed for an outage, where the service goes away, you back off further and further, and when it returns you snap back to normal cadence. Under those conditions the counter climbs, the interval grows, and the mechanism does its job.&lt;/p&gt;&#10;&lt;p&gt;Now feed it &lt;code&gt;update error update update error update&lt;/code&gt;. With a success rate around two in three, the counter never survives long enough to reach two. The cadence oscillates between the 60-second base and a single doubling, forever, permanently pinned at the limit.&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Logged cadence line&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Occurrences&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;errors=1&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;492&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;errors=4&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;5&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;492 to 5. The counter was not counting an outage, because there was no outage. Every one of those 543 rejections was the server saying &amp;ldquo;you are asking too often&amp;rdquo;, which is a completely different question, and no amount of tuning the first answer addresses the second.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Three fixes, in descending order of how much they mattered&#10; &lt;div id="three-fixes-in-descending-order-of-how-much-they-mattered" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#three-fixes-in-descending-order-of-how-much-they-mattered" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;&lt;strong&gt;One: the interval was absurd.&lt;/strong&gt; A 60-second poll for a key rendering 5-hour and 7-day windows. Those numbers move far slower than a minute. The poll is now five minutes, and the comment in the source carries the measurement that justified it:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 5 minutes, not 60s. The 5-hour and 7-day windows this renders move&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// far too slowly to justify a minute-by-minute poll, and 60s put the&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// daemon permanently at Anthropic&amp;#39;s rate limit: 1136 successes against&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// 543 HTTP 429s on this machine, arriving interleaved. With two daemons&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;// on one account it was double that.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PLAN_POLL_INTERVAL_MS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Roughly 80% of the request volume disappeared into that one constant. Everything below is smaller.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Two: obey the server.&lt;/strong&gt; Anthropic states the correct wait in &lt;code&gt;Retry-After&lt;/code&gt;, and the fetcher had been capturing that header all along, formatting it into a log string, and throwing it away. It now gets parsed and clamped, never faster than the base interval and never slower than the cap:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentIntervalMs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;retryAfterMs&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;intervalMs&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;maxBackoffMs&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;&lt;strong&gt;Three: do not poll when nobody is looking.&lt;/strong&gt; A daemon with no Stream Deck attached was polling anyway. In a two-machine setup that is the herdr host spending half the account&amp;rsquo;s request budget rendering a key that is not on screen. The gate is read per tick rather than latched at startup, so plugging a deck in does not require a daemon restart, and a skipped tick deliberately never reaches the cadence logic at all. A skipped tick is evidence of neither success nor throttling, and counting it as clean would let a headless daemon walk its way back down to an aggressive cadence without issuing a single request.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;What a review found that twelve tests did not&#10; &lt;div id="what-a-review-found-that-twelve-tests-did-not" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-a-review-found-that-twelve-tests-did-not" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;I shipped an adaptive floor alongside those fixes. Every failure raised it by 1.5x, five consecutive clean ticks earned one step back down. Back off fast, recover slowly. It had twelve tests and they all passed.&lt;/p&gt;&#10;&lt;p&gt;Then I asked for an adversarial review of the arithmetic, and got three findings, none of which a test could have caught, because in every case the tests agreed with the code.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;The tests ran against a configuration the same commit had changed.&lt;/strong&gt; The floor was tuned and verified at a 60-second base interval. The same commit moved production to 300 seconds. The cap was 10 minutes flat at the time. At 60 seconds that gives you a ladder with real rungs. At 300 seconds, the consecutive-error term is &lt;code&gt;base * 2^errors&lt;/code&gt;, so the first failure lands on 600 seconds, which is the cap exactly, and every subsequent failure lands there too.&lt;/p&gt;&#10;&lt;div class="chart"&gt;&#10; &lt;canvas data-chart="&#10;type: &amp;#39;bar&amp;#39;,&#10;data: {&#10; labels: [&amp;#39;0 errors&amp;#39;, &amp;#39;1 error&amp;#39;, &amp;#39;2 errors&amp;#39;, &amp;#39;3 errors&amp;#39;, &amp;#39;4 errors&amp;#39;],&#10; datasets: [&#10; { label: &amp;#39;60s base (where the tests ran)&amp;#39;, data: [1, 2, 4, 8, 10],&#10; backgroundColor: &amp;#39;#6366f1&amp;#39;, borderRadius: 4 },&#10; { label: &amp;#39;300s base (what shipped)&amp;#39;, data: [5, 10, 10, 10, 10],&#10; backgroundColor: &amp;#39;#f43f5e&amp;#39;, borderRadius: 4 }&#10; ]&#10;},&#10;options: {&#10; responsive: true,&#10; categoryPercentage: 0.7,&#10; barPercentage: 0.8,&#10; plugins: {&#10; legend: { labels: { color: &amp;#39;#a1a1aa&amp;#39; } },&#10; title: { display: true, text: &amp;#39;Next poll interval after N consecutive errors (minutes)&amp;#39;, color: &amp;#39;#a1a1aa&amp;#39; }&#10; },&#10; scales: {&#10; y: { title: { display: true, text: &amp;#39;Interval (minutes)&amp;#39;, color: &amp;#39;#a1a1aa&amp;#39; },&#10; ticks: { color: &amp;#39;#a1a1aa&amp;#39; }, grid: { color: &amp;#39;rgba(255,255,255,0.08)&amp;#39; } },&#10; x: { ticks: { color: &amp;#39;#a1a1aa&amp;#39; }, grid: { color: &amp;#39;rgba(255,255,255,0.08)&amp;#39; } }&#10; }&#10;}&#10;"&gt;&lt;/canvas&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;Five distinct intervals become two. The blue ladder is what twelve tests exercised. The red one is what ran on my machine. Same code, same commit, and the consecutive-error term never affected an outcome again.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;The tuning knob was mathematically inert.&lt;/strong&gt; Up-steps and down-steps were the same multiplicative size, so the constant that looked like the tuning parameter cancelled out of the stability condition entirely. Only the streak length could affect whether the mechanism converged. A knob that cannot turn anything is worse than no knob, because sooner or later somebody turns it and reasons from the result.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;And there was a reason to delete it that had nothing to do with rate limiting.&lt;/strong&gt; The floor rose on any failure, including failures that say nothing about request rate. An expired OAuth token pinned the cadence at the ceiling and kept it there for tens of minutes after I had re-authenticated.&lt;/p&gt;&#10;&lt;p&gt;So the mechanism was deleted rather than tuned, and what replaced it is less code than what it removed.&lt;/p&gt;&#10;&lt;p&gt;Two things about the review itself are worth more than the findings.&lt;/p&gt;&#10;&lt;p&gt;It was asked to attack a specific claim with the arithmetic demanded rather than the verdict: work through the failure rate, does this converge or pin at the cap? It came back with a Markov chain, a break-even threshold of 12.9%, and the observation that my convergence worry was unfounded while a different problem was fatal. A reviewer told to find problems will find some. A reviewer told to check a specific number will sometimes tell you the number is fine and the question was wrong.&lt;/p&gt;&#10;&lt;p&gt;And the reviewer corrected itself. Its first pass closed by suggesting that wiring one constructor argument would have delivered more than the whole mechanism. Asked to confirm before that shaped the fix, it checked, found that the channel it depended on does not exist anywhere in the repo, and led its second response by retracting the claim. That retraction was the most valuable paragraph in the review. A review that cannot be wrong is not a review.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The part I have to say about my own cleanup&#10; &lt;div id="the-part-i-have-to-say-about-my-own-cleanup" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-part-i-have-to-say-about-my-own-cleanup" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Given that the finding above was &amp;ldquo;you claimed a guarantee your test never made&amp;rdquo;, I am not going to claim a clean deletion.&lt;/p&gt;&#10;&lt;p&gt;The floor&amp;rsquo;s &lt;em&gt;logic&lt;/em&gt; is gone. Its residue is not. The class docstring still describes the adaptive floor as a live mechanism, in direct contradiction of the method docstring a hundred lines below it. The cap constant is still named &lt;code&gt;MAX_BACKOFF_FLOOR_MS&lt;/code&gt;. A passing test is still titled &amp;ldquo;a sustained clean streak earns the floor back&amp;rdquo;, testing something that no longer exists under a name that says it does.&lt;/p&gt;&#10;&lt;p&gt;None of that changes behaviour. All of it is exactly the kind of thing that convinces the next reader, including the next me, that a mechanism is present when it is not.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The correction I owe an earlier post&#10; &lt;div id="the-correction-i-owe-an-earlier-post" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-correction-i-owe-an-earlier-post" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;In May I wrote &lt;a href="https://nick-liu.com/posts/plan-usage-statusline-pivot/" &gt;I polled an undocumented endpoint for 18 hours. The data was on stdin.&lt;/a&gt; The argument was that &lt;code&gt;/api/oauth/usage&lt;/code&gt; was the wrong primitive and Claude Code&amp;rsquo;s statusline already pushes &lt;code&gt;rate_limits.five_hour&lt;/code&gt; and &lt;code&gt;rate_limits.seven_day&lt;/code&gt; every turn, for free.&lt;/p&gt;&#10;&lt;p&gt;The narrow claim still holds. Those two fields are documented and they are there.&lt;/p&gt;&#10;&lt;p&gt;The conclusion I drew from it overreached. I wrote that the endpoint had become &amp;ldquo;the fallback path now, not the primary one&amp;rdquo;, and HerdDeck found the inverse. The account this runs against returns seven buckets:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;five_hour, seven_day, seven_day_opus, seven_day_sonnet,&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;seven_day_cowork, seven_day_oauth_apps, seven_day_omelette&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;The per-model weekly windows have no documented statusline equivalent. The statusline is a subset of that endpoint, not a replacement for it, and if you want to know how much Opus specifically you have left this week, the undocumented endpoint is the only place that answers.&lt;/p&gt;&#10;&lt;p&gt;HerdDeck was dropping five of those seven for weeks, because the code that picked weekly buckets out of the response matched on the substring &lt;code&gt;weekly&lt;/code&gt; and every per-model key spells it &lt;code&gt;seven_day_&lt;/code&gt;. The filter now matches both halves:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/weekly/i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/^seven[_-]?day[_-]./i&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;There is a second, smaller correction. That May post called its statusline freshness gate &amp;ldquo;the load-bearing change&amp;rdquo;. HerdDeck carried the same-shaped gate forward, and it has fired &lt;strong&gt;zero times&lt;/strong&gt;. Not rarely. Zero. Nothing populates the timestamp it reads, because the statusline auto-patcher that used to populate it was one of the subsystems &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" &gt;the rebuild deleted&lt;/a&gt;.&lt;/p&gt;&#10;&lt;p&gt;I want to be precise about what that does and does not mean. It is not evidence that the gate was wrong in ClaudeDeck, where it was wired to a feeder that existed. It is that I carried the seam across a rewrite and left the feeder behind, then spent weeks suffering the exact problem the seam was built to prevent while looking straight at machinery that was inert the entire time. The gate&amp;rsquo;s docstring now says so in capital letters instead of implying a defence it does not provide.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Lessons&#10; &lt;div id="lessons" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lessons" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Consecutive-error backoff answers &amp;ldquo;is the service down&amp;rdquo;. If your failures arrive interleaved with successes, you are being told &amp;ldquo;you are asking too often&amp;rdquo;, and no amount of tuning the first answer addresses the second.&lt;/li&gt;&#10;&lt;li&gt;Look at the distribution of your error counter, not just its maximum. 492 occurrences of &lt;code&gt;errors=1&lt;/code&gt; against five of &lt;code&gt;errors=4&lt;/code&gt; says the mechanism never engaged, and it says it in one grep.&lt;/li&gt;&#10;&lt;li&gt;The server usually knows the right answer. &lt;code&gt;Retry-After&lt;/code&gt; beats any heuristic that guesses at it, and it is often already in a variable you are formatting into a log line.&lt;/li&gt;&#10;&lt;li&gt;A test suite that runs against a different constant than production is measuring a system nobody has. Check what the same commit changed before trusting what its tests proved.&lt;/li&gt;&#10;&lt;li&gt;If a tuning constant cancels out of the maths, delete it. A knob that cannot turn anything is worse than no knob, because someone will turn it and believe the result.&lt;/li&gt;&#10;&lt;li&gt;Deleting a mechanism means deleting the docstrings, constant names and test titles that advertise it. Residue outlives behaviour and it is what the next reader believes.&lt;/li&gt;&#10;&lt;li&gt;When you port a design across a rewrite, port the thing that feeds it or delete the seam. A defence with no input is indistinguishable from a defence, right up until you need it.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;References&#10; &lt;div id="references" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#references" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Claude Code statusline docs, the documented &lt;code&gt;rate_limits&lt;/code&gt; contract: &lt;a href="https://code.claude.com/docs/en/statusline" target="_blank" rel="noreferrer"&gt;https://code.claude.com/docs/en/statusline&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;RFC 9110 section 10.2.3, &lt;code&gt;Retry-After&lt;/code&gt; semantics: &lt;a href="https://httpwg.org/specs/rfc9110.html#field.retry-after" target="_blank" rel="noreferrer"&gt;https://httpwg.org/specs/rfc9110.html#field.retry-after&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The post this one corrects: &lt;a href="https://nick-liu.com/posts/plan-usage-statusline-pivot/" &gt;I polled an undocumented endpoint for 18 hours. The data was on stdin.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The rebuild that deleted the statusline auto-patcher: &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" &gt;I deleted six subsystems by swapping one dependency.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;HerdDeck poller and cadence logic: &lt;a href="https://github.com/nickboy/herddeck/blob/main/packages/daemon/src/planUsagePoller.ts" target="_blank" rel="noreferrer"&gt;&lt;code&gt;packages/daemon/src/planUsagePoller.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;HerdDeck usage fetcher, &lt;code&gt;Retry-After&lt;/code&gt; parsing and the weekly-bucket filter: &lt;a href="https://github.com/nickboy/herddeck/blob/main/packages/daemon/src/claudeAiFetcher.ts" target="_blank" rel="noreferrer"&gt;&lt;code&gt;packages/daemon/src/claudeAiFetcher.ts&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item><item><title>I deleted six subsystems by swapping one dependency. The protocol billed me.</title><link>https://nick-liu.com/posts/herddeck-substrate-bet/</link><pubDate>Sun, 30 Aug 2026 00:00:00 +0000</pubDate><author>nickboy@users.noreply.github.com (Nick Liu)</author><guid>https://nick-liu.com/posts/herddeck-substrate-bet/</guid><description>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;Six of the ten posts in my "Building ClaudeDeck" series document code that no longer exists. Over four days in August I rebuilt the plugin on herdr's socket API, and the hook dispatcher, the PTY runner, the statusline auto-patcher, the AppleScript focus path, the Claude project watcher and the shell-PID resolver all went in the bin, taking the `.app` bundle, the codesigning step and every TCC prompt with them. Then the substrate sent its invoice.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 1 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me." aria-current="step"&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question."&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other."&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%."&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end."&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;herdr&lt;/strong&gt; &lt;code&gt;0.8.0&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;ClaudeDeck was complicated for exactly one reason. Claude Code has no control API, so every fact the plugin needed had to be stolen from somewhere it was not offered.&lt;/p&gt;</description><content:encoded>&lt;div class="lead text-neutral-500 dark:text-neutral-400 !mb-9 text-xl"&gt;&#10; &#10;Six of the ten posts in my "Building ClaudeDeck" series document code that no longer exists. Over four days in August I rebuilt the plugin on herdr's socket API, and the hook dispatcher, the PTY runner, the statusline auto-patcher, the AppleScript focus path, the Claude project watcher and the shell-PID resolver all went in the bin, taking the `.app` bundle, the codesigning step and every TCC prompt with them. Then the substrate sent its invoice.&#10;&#10;&lt;/div&gt;&#10;&#10;&lt;nav class="series-progress" aria-label="Series progress"&gt;&#10; &lt;span class="series-progress-label"&gt;Deleting ClaudeDeck · Part 1 of 5&lt;/span&gt;&#10; &lt;ol class="series-progress-dots"&gt;&lt;li class="series-progress-dot is-current"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-substrate-bet/" title="I deleted six subsystems by swapping one dependency. The protocol billed me." aria-current="step"&gt;1&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-backoff-wrong-question/" title="My backoff logged errors=1 492 times. It was answering the wrong question."&gt;2&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-frozen-context-ring/" title="Every context ring read 10%. Two bugs, and fixing one hid the other."&gt;3&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ring-read-backwards/" title="The ring was 1.00:1 against its background. The key read 35% at 65%."&gt;4&lt;/a&gt;&#10; &lt;/li&gt;&lt;li class="series-progress-dot"&gt;&#10; &lt;a href="https://nick-liu.com/posts/herddeck-ssh-tunnel-lies/" title="The tunnel was up. The socket existed. Nothing was on the other end."&gt;5&lt;/a&gt;&#10; &lt;/li&gt;&lt;/ol&gt;&#10; &lt;/nav&gt;&#10;&lt;div class="tested-with" role="note"&gt;&#10; &lt;span class="tested-with-icon" aria-hidden="true"&gt;🧪&lt;/span&gt;&#10; &lt;span&gt;Tested with &lt;strong&gt;herdr&lt;/strong&gt; &lt;code&gt;0.8.0&lt;/code&gt; · macOS&lt;/span&gt;&#10;&lt;/div&gt;&#10;&#10;&lt;p&gt;ClaudeDeck was complicated for exactly one reason. Claude Code has no control API, so every fact the plugin needed had to be stolen from somewhere it was not offered.&lt;/p&gt;&#10;&lt;p&gt;To know an agent was blocked, it installed hooks and dispatched them. To know how full the context window was, it patched the statusline. To read what a pane was showing, it ran a PTY and kept a ring buffer. To jump to the right terminal tab it drove AppleScript, which meant Accessibility, which meant TCC, which meant codesigning. To know which project a shell belonged to, it resolved shell PIDs and watched the projects directory.&lt;/p&gt;&#10;&lt;p&gt;&lt;a href="https://herdr.dev" target="_blank" rel="noreferrer"&gt;herdr&lt;/a&gt; is a terminal multiplexer built for coding agents. It already tracks all of that, and it exposes it on a Unix socket speaking NDJSON. Agent lifecycle, &lt;code&gt;idle&lt;/code&gt; and &lt;code&gt;working&lt;/code&gt; and &lt;code&gt;blocked&lt;/code&gt; and &lt;code&gt;done&lt;/code&gt;, is a first-class concept in the protocol rather than something you infer.&lt;/p&gt;&#10;&lt;p&gt;So the interesting artifact of this rebuild is not what I wrote. It is what came out.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;What all that code was for&#10; &lt;div id="what-all-that-code-was-for" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-all-that-code-was-for" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;ClaudeDeck subsystem&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;It existed because&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;HerdDeck instead&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Hook dispatch (&lt;a href="https://nick-liu.com/posts/claude-code-hooks-reality/" &gt;post&lt;/a&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;nothing tells you an agent is blocked&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;agent_status&lt;/code&gt; is a field in the protocol&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Permission round-trip (&lt;a href="https://nick-liu.com/posts/permission-round-trip/" &gt;post&lt;/a&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;approval meant parking an HTTP response on a Promise&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;herdr already knows a pane is waiting&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Statusline auto-patcher (&lt;a href="https://nick-liu.com/posts/statusline-side-channel/" &gt;post&lt;/a&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;context fullness had to be smuggled out per turn&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;a statusline delegate you install yourself, never patched in behind your back&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;PTY runner and ring buffer (&lt;a href="https://nick-liu.com/posts/pty-wrap-migration/" &gt;post&lt;/a&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;reading a pane meant owning the terminal&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;herdr owns the terminal&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;AppleScript, codesigning, TCC (&lt;a href="https://nick-liu.com/posts/tcc-cdhash-trap/" &gt;post&lt;/a&gt;)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;focusing a tab needed Accessibility&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;one socket call&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Shell-PID resolver, project watcher&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;mapping a shell to a project&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;herdr tracks it&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Two things fell out of that beyond the line count, and only one of them was planned.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;It works for every agent herdr recognises.&lt;/strong&gt; Claude Code, Codex, OpenCode. The old design was Claude-shaped because hooks are Claude-shaped. The new one asks herdr what is running and gets an answer that was never Claude-specific in the first place.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Remote came close to free.&lt;/strong&gt; herdr splits client from server, so the server holds the state and a remote server speaks the identical protocol. Forward the socket over SSH and the same daemon code drives agents on another machine.&lt;/p&gt;&#10;&lt;div class="not-prose diagram diagram-light"&gt;&lt;svg id="d21793c5c62197893light" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 1228.5px;" viewBox="0 0 1228.5 94" role="graphics-document document" aria-roledescription="flowchart-v2"&gt;&lt;style&gt;#d21793c5c62197893light{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#d21793c5c62197893light .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#d21793c5c62197893light .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#d21793c5c62197893light .error-icon{fill:rgb(255, 255, 255);}#d21793c5c62197893light .error-text{fill:#000000;stroke:#000000;}#d21793c5c62197893light .edge-thickness-normal{stroke-width:1px;}#d21793c5c62197893light .edge-thickness-thick{stroke-width:3.5px;}#d21793c5c62197893light .edge-pattern-solid{stroke-dasharray:0;}#d21793c5c62197893light .edge-thickness-invisible{stroke-width:0;fill:none;}#d21793c5c62197893light .edge-pattern-dashed{stroke-dasharray:3;}#d21793c5c62197893light .edge-pattern-dotted{stroke-dasharray:2;}#d21793c5c62197893light .marker{fill:rgb(74, 86, 92);stroke:rgb(74, 86, 92);}#d21793c5c62197893light .marker.cross{stroke:rgb(74, 86, 92);}#d21793c5c62197893light svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#d21793c5c62197893light p{margin:0;}#d21793c5c62197893light .label{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;color:#333;}#d21793c5c62197893light .cluster-label text{fill:#000000;}#d21793c5c62197893light .cluster-label span{color:#000000;}#d21793c5c62197893light .cluster-label span p{background-color:transparent;}#d21793c5c62197893light .label text,#d21793c5c62197893light span{fill:#333;color:#333;}#d21793c5c62197893light .node rect,#d21793c5c62197893light .node circle,#d21793c5c62197893light .node ellipse,#d21793c5c62197893light .node polygon,#d21793c5c62197893light .node path{fill:rgb(255, 255, 255);stroke:rgb(204, 216, 222);stroke-width:1px;}#d21793c5c62197893light .rough-node .label text,#d21793c5c62197893light .node .label text,#d21793c5c62197893light .image-shape .label,#d21793c5c62197893light .icon-shape .label{text-anchor:middle;}#d21793c5c62197893light .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#d21793c5c62197893light .rough-node .label,#d21793c5c62197893light .node .label,#d21793c5c62197893light .image-shape .label,#d21793c5c62197893light .icon-shape .label{text-align:center;}#d21793c5c62197893light .node.clickable{cursor:pointer;}#d21793c5c62197893light .root .anchor path{fill:rgb(74, 86, 92)!important;stroke-width:0;stroke:rgb(74, 86, 92);}#d21793c5c62197893light .arrowheadPath{fill:#000000;}#d21793c5c62197893light .edgePath .path{stroke:rgb(74, 86, 92);stroke-width:1px;}#d21793c5c62197893light .flowchart-link{stroke:rgb(74, 86, 92);fill:none;}#d21793c5c62197893light .edgeLabel{background-color:rgb(182, 240, 255);text-align:center;}#d21793c5c62197893light .edgeLabel p{background-color:rgb(182, 240, 255);}#d21793c5c62197893light .edgeLabel rect{opacity:0.5;background-color:rgb(182, 240, 255);fill:rgb(182, 240, 255);}#d21793c5c62197893light .labelBkg{background-color:rgba(182, 240, 255, 0.5);}#d21793c5c62197893light .cluster rect{fill:rgb(255, 255, 255);stroke:rgb(129, 146, 154);stroke-width:1px;}#d21793c5c62197893light .cluster text{fill:#000000;}#d21793c5c62197893light .cluster span{color:#000000;}#d21793c5c62197893light div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:12px;background:rgb(255, 255, 255);border:1px solid rgb(129, 146, 154);border-radius:2px;pointer-events:none;z-index:100;}#d21793c5c62197893light .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#d21793c5c62197893light rect.text{fill:none;stroke-width:0;}#d21793c5c62197893light .icon-shape,#d21793c5c62197893light .image-shape{background-color:rgb(182, 240, 255);text-align:center;}#d21793c5c62197893light .icon-shape p,#d21793c5c62197893light .image-shape p{background-color:rgb(182, 240, 255);padding:2px;}#d21793c5c62197893light .icon-shape .label rect,#d21793c5c62197893light .image-shape .label rect{opacity:0.5;background-color:rgb(182, 240, 255);fill:rgb(182, 240, 255);}#d21793c5c62197893light .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#d21793c5c62197893light .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#d21793c5c62197893light .node .neo-node{stroke:rgb(204, 216, 222);}#d21793c5c62197893light [data-look="neo"].node rect,#d21793c5c62197893light [data-look="neo"].cluster rect,#d21793c5c62197893light [data-look="neo"].node polygon{stroke:url(#d21793c5c62197893light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893light [data-look="neo"].swimlane.cluster rect{filter:none;}#d21793c5c62197893light [data-look="neo"].node path{stroke:url(#d21793c5c62197893light-gradient);stroke-width:1px;}#d21793c5c62197893light [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893light [data-look="neo"].node .neo-line path{stroke:rgb(204, 216, 222);filter:none;}#d21793c5c62197893light [data-look="neo"].node circle{stroke:url(#d21793c5c62197893light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893light [data-look="neo"].node circle .state-start{fill:#000000;}#d21793c5c62197893light [data-look="neo"].icon-shape .icon{fill:url(#d21793c5c62197893light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893light [data-look="neo"].icon-shape .icon-neo path{stroke:url(#d21793c5c62197893light-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893light :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"&gt;&lt;path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"&gt;&lt;polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893light_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class="root"&gt;&lt;g class="clusters"&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M202.781,47L206.948,47C211.115,47,219.448,47,227.115,47C234.781,47,241.781,47,245.281,47L248.781,47" id="d21793c5c62197893light-L_SD_P_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_SD_P_0" data-points="W3sieCI6MjAyLjc4MTI1LCJ5Ijo0N30seyJ4IjoyMjcuNzgxMjUsInkiOjQ3fSx7IngiOjI1Mi43ODEyNSwieSI6NDd9XQ==" data-look="classic" marker-end="url(#d21793c5c62197893light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M357.094,47L368.154,47C379.214,47,401.333,47,422.786,47C444.24,47,465.026,47,475.419,47L485.813,47" id="d21793c5c62197893light-L_P_D_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_P_D_0" data-points="W3sieCI6MzU3LjA5Mzc1LCJ5Ijo0N30seyJ4Ijo0MjMuNDUzMTI1LCJ5Ijo0N30seyJ4Ijo0ODkuODEyNSwieSI6NDd9XQ==" data-look="classic" marker-end="url(#d21793c5c62197893light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M680.984,47L696.965,47C712.945,47,744.906,47,776.201,47C807.495,47,838.122,47,853.436,47L868.75,47" id="d21793c5c62197893light-L_D_H_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_D_H_0" data-points="W3sieCI6NjgwLjk4NDM3NSwieSI6NDd9LHsieCI6Nzc2Ljg2NzE4NzUsInkiOjQ3fSx7IngiOjg3Mi43NSwieSI6NDd9XQ==" data-look="classic" marker-end="url(#d21793c5c62197893light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M1021.125,47L1025.292,47C1029.458,47,1037.792,47,1045.458,47C1053.125,47,1060.125,47,1063.625,47L1067.125,47" id="d21793c5c62197893light-L_H_A1_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_H_A1_0" data-points="W3sieCI6MTAyMS4xMjUsInkiOjQ3fSx7IngiOjEwNDYuMTI1LCJ5Ijo0N30seyJ4IjoxMDcxLjEyNSwieSI6NDd9XQ==" data-look="classic" marker-end="url(#d21793c5c62197893light_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_SD_P_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel" transform="translate(423.453125, 47)"&gt;&lt;g class="label" data-id="L_P_D_0" transform="translate(-41.359375, -12)"&gt;&lt;foreignObject width="82.71875" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;p&gt;WebSocket&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel" transform="translate(776.8671875, 47)"&gt;&lt;g class="label" data-id="L_D_H_0" transform="translate(-70.8828125, -24)"&gt;&lt;foreignObject width="141.765625" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;p&gt;ssh -N -L&lt;br&gt;unix socket forward&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_H_A1_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="d21793c5c62197893light-flowchart-SD-0" data-look="classic" transform="translate(105.390625, 47)"&gt;&lt;rect class="basic label-container" style="" x="-97.390625" y="-27" width="194.78125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-67.390625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="134.78125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Stream Deck MK.2&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="d21793c5c62197893light-flowchart-P-1" data-look="classic" transform="translate(304.9375, 47)"&gt;&lt;rect class="basic label-container" style="" x="-52.15625" y="-27" width="104.3125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-22.15625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="44.3125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;plugin&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="d21793c5c62197893light-flowchart-D-3" data-look="classic" transform="translate(585.3984375, 47)"&gt;&lt;rect class="basic label-container" style="" x="-95.5859375" y="-39" width="191.171875" height="78"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-65.5859375, -24)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="131.171875" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;herddeck daemon&lt;br&gt;(laptop, launchd)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="d21793c5c62197893light-flowchart-H-5" data-look="classic" transform="translate(946.9375, 47)"&gt;&lt;rect class="basic label-container" style="" x="-74.1875" y="-39" width="148.375" height="78"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-44.1875, -24)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="88.375" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;herdr server&lt;br&gt;(desktop)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="d21793c5c62197893light-flowchart-A1-7" data-look="classic" transform="translate(1145.8125, 47)"&gt;&lt;rect class="basic label-container" style="" x="-74.6875" y="-27" width="149.375" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-44.6875, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="89.375" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;agent panes&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id="d21793c5c62197893light-drop-shadow" height="130%" width="130%"&gt;&lt;feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id="d21793c5c62197893light-drop-shadow-small" height="150%" width="150%"&gt;&lt;feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#000000"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;linearGradient id="d21793c5c62197893light-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"&gt;&lt;stop offset="0%" stop-color="rgb(204, 216, 222)" stop-opacity="1"&gt;&lt;/stop&gt;&lt;stop offset="100%" stop-color="rgb(28, 209, 255)" stop-opacity="1"&gt;&lt;/stop&gt;&lt;/linearGradient&gt;&lt;/svg&gt;&lt;/div&gt;&#10; &lt;div class="not-prose diagram diagram-dark"&gt;&lt;svg id="d21793c5c62197893dark" width="100%" xmlns="http://www.w3.org/2000/svg" class="flowchart" style="max-width: 1228.5px;" viewBox="0 0 1228.5 94" role="graphics-document document" aria-roledescription="flowchart-v2"&gt;&lt;style&gt;#d21793c5c62197893dark{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#ccc;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#d21793c5c62197893dark .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#d21793c5c62197893dark .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#d21793c5c62197893dark .error-icon{fill:#a44141;}#d21793c5c62197893dark .error-text{fill:#ddd;stroke:#ddd;}#d21793c5c62197893dark .edge-thickness-normal{stroke-width:1px;}#d21793c5c62197893dark .edge-thickness-thick{stroke-width:3.5px;}#d21793c5c62197893dark .edge-pattern-solid{stroke-dasharray:0;}#d21793c5c62197893dark .edge-thickness-invisible{stroke-width:0;fill:none;}#d21793c5c62197893dark .edge-pattern-dashed{stroke-dasharray:3;}#d21793c5c62197893dark .edge-pattern-dotted{stroke-dasharray:2;}#d21793c5c62197893dark .marker{fill:lightgrey;stroke:lightgrey;}#d21793c5c62197893dark .marker.cross{stroke:lightgrey;}#d21793c5c62197893dark svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#d21793c5c62197893dark p{margin:0;}#d21793c5c62197893dark .label{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;color:#ccc;}#d21793c5c62197893dark .cluster-label text{fill:#F9FFFE;}#d21793c5c62197893dark .cluster-label span{color:#F9FFFE;}#d21793c5c62197893dark .cluster-label span p{background-color:transparent;}#d21793c5c62197893dark .label text,#d21793c5c62197893dark span{fill:#ccc;color:#ccc;}#d21793c5c62197893dark .node rect,#d21793c5c62197893dark .node circle,#d21793c5c62197893dark .node ellipse,#d21793c5c62197893dark .node polygon,#d21793c5c62197893dark .node path{fill:#1f2020;stroke:#ccc;stroke-width:1px;}#d21793c5c62197893dark .rough-node .label text,#d21793c5c62197893dark .node .label text,#d21793c5c62197893dark .image-shape .label,#d21793c5c62197893dark .icon-shape .label{text-anchor:middle;}#d21793c5c62197893dark .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#d21793c5c62197893dark .rough-node .label,#d21793c5c62197893dark .node .label,#d21793c5c62197893dark .image-shape .label,#d21793c5c62197893dark .icon-shape .label{text-align:center;}#d21793c5c62197893dark .node.clickable{cursor:pointer;}#d21793c5c62197893dark .root .anchor path{fill:lightgrey!important;stroke-width:0;stroke:lightgrey;}#d21793c5c62197893dark .arrowheadPath{fill:lightgrey;}#d21793c5c62197893dark .edgePath .path{stroke:lightgrey;stroke-width:1px;}#d21793c5c62197893dark .flowchart-link{stroke:lightgrey;fill:none;}#d21793c5c62197893dark .edgeLabel{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#d21793c5c62197893dark .edgeLabel p{background-color:hsl(0, 0%, 34.4117647059%);}#d21793c5c62197893dark .edgeLabel rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#d21793c5c62197893dark .labelBkg{background-color:rgba(87.75, 87.75, 87.75, 0.5);}#d21793c5c62197893dark .cluster rect{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:rgba(255, 255, 255, 0.25);stroke-width:1px;}#d21793c5c62197893dark .cluster text{fill:#F9FFFE;}#d21793c5c62197893dark .cluster span{color:#F9FFFE;}#d21793c5c62197893dark div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:12px;background:hsl(20, 1.5873015873%, 12.3529411765%);border:1px solid rgba(255, 255, 255, 0.25);border-radius:2px;pointer-events:none;z-index:100;}#d21793c5c62197893dark .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#ccc;}#d21793c5c62197893dark rect.text{fill:none;stroke-width:0;}#d21793c5c62197893dark .icon-shape,#d21793c5c62197893dark .image-shape{background-color:hsl(0, 0%, 34.4117647059%);text-align:center;}#d21793c5c62197893dark .icon-shape p,#d21793c5c62197893dark .image-shape p{background-color:hsl(0, 0%, 34.4117647059%);padding:2px;}#d21793c5c62197893dark .icon-shape .label rect,#d21793c5c62197893dark .image-shape .label rect{opacity:0.5;background-color:hsl(0, 0%, 34.4117647059%);fill:hsl(0, 0%, 34.4117647059%);}#d21793c5c62197893dark .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#d21793c5c62197893dark .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#d21793c5c62197893dark .node .neo-node{stroke:#ccc;}#d21793c5c62197893dark [data-look="neo"].node rect,#d21793c5c62197893dark [data-look="neo"].cluster rect,#d21793c5c62197893dark [data-look="neo"].node polygon{stroke:url(#d21793c5c62197893dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893dark [data-look="neo"].swimlane.cluster rect{filter:none;}#d21793c5c62197893dark [data-look="neo"].node path{stroke:url(#d21793c5c62197893dark-gradient);stroke-width:1px;}#d21793c5c62197893dark [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893dark [data-look="neo"].node .neo-line path{stroke:#ccc;filter:none;}#d21793c5c62197893dark [data-look="neo"].node circle{stroke:url(#d21793c5c62197893dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893dark [data-look="neo"].node circle .state-start{fill:#000000;}#d21793c5c62197893dark [data-look="neo"].icon-shape .icon{fill:url(#d21793c5c62197893dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893dark [data-look="neo"].icon-shape .icon-neo path{stroke:url(#d21793c5c62197893dark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#d21793c5c62197893dark :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-pointEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 0 L 10 5 L 0 10 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-pointStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="4.5" refY="5" markerUnits="userSpaceOnUse" markerWidth="8" markerHeight="8" orient="auto"&gt;&lt;path d="M 0 5 L 10 10 L 10 0 z" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-pointEnd-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="11.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="10.5" markerHeight="14" orient="auto"&gt;&lt;path d="M 0 0 L 11.5 7 L 0 14 z" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-pointStart-margin" class="marker flowchart-v2" viewBox="0 0 11.5 14" refX="1" refY="7" markerUnits="userSpaceOnUse" markerWidth="11.5" markerHeight="14" orient="auto"&gt;&lt;polygon points="0,7 11.5,14 11.5,0" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/polygon&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-circleEnd" class="marker flowchart-v2" viewBox="0 0 10 10" refX="11" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-circleStart" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-1" refY="5" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 1; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-circleEnd-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refY="5" refX="12.25" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-circleStart-margin" class="marker flowchart-v2" viewBox="0 0 10 10" refX="-2" refY="5" markerUnits="userSpaceOnUse" markerWidth="14" markerHeight="14" orient="auto"&gt;&lt;circle cx="5" cy="5" r="5" class="arrowMarkerPath" style="stroke-width: 0; stroke-dasharray: 1, 0;"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-crossEnd" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="12" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-crossStart" class="marker cross flowchart-v2" viewBox="0 0 11 11" refX="-1" refY="5.2" markerUnits="userSpaceOnUse" markerWidth="11" markerHeight="11" orient="auto"&gt;&lt;path d="M 1,1 l 9,9 M 10,1 l -9,9" class="arrowMarkerPath" style="stroke-width: 2; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-crossEnd-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="17.7" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;marker id="d21793c5c62197893dark_flowchart-v2-crossStart-margin" class="marker cross flowchart-v2" viewBox="0 0 15 15" refX="-3.5" refY="7.5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto"&gt;&lt;path d="M 1,1 L 14,14 M 1,14 L 14,1" class="arrowMarkerPath" style="stroke-width: 2.5; stroke-dasharray: 1, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;g class="root"&gt;&lt;g class="clusters"&gt;&lt;/g&gt;&lt;g class="edgePaths"&gt;&lt;path d="M202.781,47L206.948,47C211.115,47,219.448,47,227.115,47C234.781,47,241.781,47,245.281,47L248.781,47" id="d21793c5c62197893dark-L_SD_P_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_SD_P_0" data-points="W3sieCI6MjAyLjc4MTI1LCJ5Ijo0N30seyJ4IjoyMjcuNzgxMjUsInkiOjQ3fSx7IngiOjI1Mi43ODEyNSwieSI6NDd9XQ==" data-look="classic" marker-end="url(#d21793c5c62197893dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M357.094,47L368.154,47C379.214,47,401.333,47,422.786,47C444.24,47,465.026,47,475.419,47L485.813,47" id="d21793c5c62197893dark-L_P_D_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_P_D_0" data-points="W3sieCI6MzU3LjA5Mzc1LCJ5Ijo0N30seyJ4Ijo0MjMuNDUzMTI1LCJ5Ijo0N30seyJ4Ijo0ODkuODEyNSwieSI6NDd9XQ==" data-look="classic" marker-end="url(#d21793c5c62197893dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M680.984,47L696.965,47C712.945,47,744.906,47,776.201,47C807.495,47,838.122,47,853.436,47L868.75,47" id="d21793c5c62197893dark-L_D_H_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_D_H_0" data-points="W3sieCI6NjgwLjk4NDM3NSwieSI6NDd9LHsieCI6Nzc2Ljg2NzE4NzUsInkiOjQ3fSx7IngiOjg3Mi43NSwieSI6NDd9XQ==" data-look="classic" marker-end="url(#d21793c5c62197893dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;path d="M1021.125,47L1025.292,47C1029.458,47,1037.792,47,1045.458,47C1053.125,47,1060.125,47,1063.625,47L1067.125,47" id="d21793c5c62197893dark-L_H_A1_0" class="edge-thickness-normal edge-pattern-solid edge-thickness-normal edge-pattern-solid flowchart-link" style=";" data-edge="true" data-et="edge" data-id="L_H_A1_0" data-points="W3sieCI6MTAyMS4xMjUsInkiOjQ3fSx7IngiOjEwNDYuMTI1LCJ5Ijo0N30seyJ4IjoxMDcxLjEyNSwieSI6NDd9XQ==" data-look="classic" marker-end="url(#d21793c5c62197893dark_flowchart-v2-pointEnd)"&gt;&lt;/path&gt;&lt;/g&gt;&lt;g class="edgeLabels"&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_SD_P_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel" transform="translate(423.453125, 47)"&gt;&lt;g class="label" data-id="L_P_D_0" transform="translate(-41.359375, -12)"&gt;&lt;foreignObject width="82.71875" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;p&gt;WebSocket&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel" transform="translate(776.8671875, 47)"&gt;&lt;g class="label" data-id="L_D_H_0" transform="translate(-70.8828125, -24)"&gt;&lt;foreignObject width="141.765625" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;p&gt;ssh -N -L&lt;br&gt;unix socket forward&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="edgeLabel"&gt;&lt;g class="label" data-id="L_H_A1_0" transform="translate(0, 0)"&gt;&lt;foreignObject width="0" height="0"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" class="labelBkg" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="edgeLabel"&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="nodes"&gt;&lt;g class="node default" id="d21793c5c62197893dark-flowchart-SD-0" data-look="classic" transform="translate(105.390625, 47)"&gt;&lt;rect class="basic label-container" style="" x="-97.390625" y="-27" width="194.78125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-67.390625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="134.78125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;Stream Deck MK.2&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="d21793c5c62197893dark-flowchart-P-1" data-look="classic" transform="translate(304.9375, 47)"&gt;&lt;rect class="basic label-container" style="" x="-52.15625" y="-27" width="104.3125" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-22.15625, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="44.3125" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;plugin&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="d21793c5c62197893dark-flowchart-D-3" data-look="classic" transform="translate(585.3984375, 47)"&gt;&lt;rect class="basic label-container" style="" x="-95.5859375" y="-39" width="191.171875" height="78"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-65.5859375, -24)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="131.171875" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;herddeck daemon&lt;br&gt;(laptop, launchd)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="d21793c5c62197893dark-flowchart-H-5" data-look="classic" transform="translate(946.9375, 47)"&gt;&lt;rect class="basic label-container" style="" x="-74.1875" y="-39" width="148.375" height="78"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-44.1875, -24)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="88.375" height="48"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;herdr server&lt;br&gt;(desktop)&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;g class="node default" id="d21793c5c62197893dark-flowchart-A1-7" data-look="classic" transform="translate(1145.8125, 47)"&gt;&lt;rect class="basic label-container" style="" x="-74.6875" y="-27" width="149.375" height="54"&gt;&lt;/rect&gt;&lt;g class="label" style="" transform="translate(-44.6875, -12)"&gt;&lt;rect&gt;&lt;/rect&gt;&lt;foreignObject width="89.375" height="24"&gt;&lt;div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; line-height: 1.5; max-width: 200px; text-align: center;"&gt;&lt;span class="nodeLabel"&gt;&lt;p&gt;agent panes&lt;/p&gt;&lt;/span&gt;&lt;/div&gt;&lt;/foreignObject&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;/g&gt;&lt;defs&gt;&lt;filter id="d21793c5c62197893dark-drop-shadow" height="130%" width="130%"&gt;&lt;feDropShadow dx="4" dy="4" stdDeviation="0" flood-opacity="0.06" flood-color="#FFFFFF"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;defs&gt;&lt;filter id="d21793c5c62197893dark-drop-shadow-small" height="150%" width="150%"&gt;&lt;feDropShadow dx="2" dy="2" stdDeviation="0" flood-opacity="0.06" flood-color="#FFFFFF"&gt;&lt;/feDropShadow&gt;&lt;/filter&gt;&lt;/defs&gt;&lt;linearGradient id="d21793c5c62197893dark-gradient" gradientUnits="objectBoundingBox" x1="0%" y1="0%" x2="100%" y2="0%"&gt;&lt;stop offset="0%" stop-color="#cccccc" stop-opacity="1"&gt;&lt;/stop&gt;&lt;stop offset="100%" stop-color="hsl(180, 0%, 18.3529411765%)" stop-opacity="1"&gt;&lt;/stop&gt;&lt;/linearGradient&gt;&lt;/svg&gt;&lt;/div&gt;&#10;&lt;p&gt;I want to be careful about the word &amp;ldquo;free&amp;rdquo;, because there is a caveat and the obvious guess about it is wrong. &lt;code&gt;herdr --remote&lt;/code&gt; makes the human terminal experience a thin client, but it does not expose a local API socket proxying the remote server. The local &lt;code&gt;herdr-client.sock&lt;/code&gt; is a TUI attach endpoint and nothing more. The daemon needs its own forward. And the auto-sync that keeps herdr versions matched only fires on a human &lt;code&gt;--remote&lt;/code&gt; attach, never on the daemon&amp;rsquo;s forward, so a box you rarely attach to can quietly drift to an older protocol. Every target therefore gets pinged and version-checked on connect and drops to a warning state on mismatch, rather than being assumed compatible.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The bet, and it was a bet&#10; &lt;div id="the-bet-and-it-was-a-bet" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-bet-and-it-was-a-bet" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;I would rather leave this part out.&lt;/p&gt;&#10;&lt;p&gt;I have &lt;a href="https://nick-liu.com/posts/herdr-trial-agent-multiplexer/" &gt;a post on this blog about trialling herdr&lt;/a&gt;, with exit conditions written down before the trial started and a verdict due on August 16.&lt;/p&gt;&#10;&lt;p&gt;HerdDeck was built between August 6 and August 9.&lt;/p&gt;&#10;&lt;p&gt;So I deleted a working codebase and rebuilt my hardware controller on a substrate that was still on probation, a week before the trial I had written exit conditions for was due to conclude. If the verdict had gone the other way, the sunk cost would have been the whole rebuild plus the ClaudeDeck code I had already thrown away.&lt;/p&gt;&#10;&lt;p&gt;The verdict has since landed and it went my way. herdr graduated, which is the least interesting thing about it. It graduated on two of its three conditions, because the third turned out to be unanswerable from inside the window: a month of deliberately using one tool makes that tool look load-bearing whichever way the comparison would actually have gone. The trial is the data.&lt;/p&gt;&#10;&lt;p&gt;That is the same problem I had already created, one layer down and worse. Building HerdDeck did not merely make herdr look useful. It made herdr useful, to me, on hardware I reach for every day. A trial gets harder to fail once you have built something on the thing being trialled, and I built four days of work on it before the verdict was in.&lt;/p&gt;&#10;&lt;p&gt;A favourable answer does not make the sequence defensible and I am not going to dress it up as one. What makes it survivable rather than reckless is narrow: the deletion is reversible in the only sense that matters, because ClaudeDeck is still a repository and still works. What it cost is a real option, the ability to evaluate herdr without also evaluating four days of my own work sitting on top of it.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;What the protocol charged&#10; &lt;div id="what-the-protocol-charged" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-the-protocol-charged" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;These are the things you cannot learn by reading a schema, and they are the actual price of the substrate.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;One request per connection.&lt;/strong&gt; herdr answers the first NDJSON line on a connection and closes it. &lt;code&gt;events.subscribe&lt;/code&gt; is the exception: it converts its connection into a long-lived stream with a fixed subscription set. A client that wants to change what it is subscribed to has to open a new stream, which turns every subscription change into a make-before-break problem rather than a mutation.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Event names are inconsistent.&lt;/strong&gt; Lifecycle pushes use underscores, like &lt;code&gt;pane_created&lt;/code&gt;, with &lt;code&gt;data.type&lt;/code&gt; repeating the name. Status pushes use dots, like &lt;code&gt;pane.agent_status_changed&lt;/code&gt;. The cache accepts both spellings so no caller has to normalise first, which is a workaround rather than a fix.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Container closes do not cascade into pane events.&lt;/strong&gt; Closing a workspace or a tab emits &lt;code&gt;workspace_closed&lt;/code&gt; or &lt;code&gt;tab_closed&lt;/code&gt; and nothing else. No &lt;code&gt;pane_closed&lt;/code&gt; for the panes inside. A cache that only listens for pane events keeps zombie entries forever, which is exactly what mine did until I noticed.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&lt;code&gt;workspace.close&lt;/code&gt; is asynchronous.&lt;/strong&gt; The &lt;code&gt;ok&lt;/code&gt; response returns immediately. The pane processes take one to two seconds to wind down before &lt;code&gt;workspace_closed&lt;/code&gt; fires.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;New subscribers get synthetic replay.&lt;/strong&gt; A fresh &lt;code&gt;events.subscribe&lt;/code&gt; stream receives &lt;code&gt;pane_created&lt;/code&gt; for panes that already existed. This is harmless under the ordering below, and it looks exactly like corruption if you are not expecting it.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Per-pane subscriptions are all-or-nothing.&lt;/strong&gt; &lt;code&gt;pane.agent_status_changed&lt;/code&gt; requires a &lt;code&gt;pane_id&lt;/code&gt; and there is no wildcard. One stale id fails the entire batch. That one produced the most instructive bug in the project, and it is the rest of this post.&lt;/p&gt;&#10;&lt;p&gt;None of the above is a criticism of herdr, which is a good substrate and the reason the rebuild was worth attempting. It is the answer to the question &amp;ldquo;what did it cost&amp;rdquo;, which is the question a post like this usually skips.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;The ordering that took three tries&#10; &lt;div id="the-ordering-that-took-three-tries" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#the-ordering-that-took-three-tries" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;The naive connect sequence is: snapshot the state, then subscribe to changes. That loses every transition landing in the gap between the two. The reverse, subscribe then snapshot, double-counts.&lt;/p&gt;&#10;&lt;p&gt;What actually works has five steps, and step four is the load-bearing one.&lt;/p&gt;&#10;&lt;div class="not-prose diagram diagram-light"&gt;&lt;svg id="dcd14d06924d1858alight" width="100%" xmlns="http://www.w3.org/2000/svg" style="max-width: 542.5px;" viewBox="-81.5 -10 542.5 579" role="graphics-document document" aria-roledescription="sequence"&gt;&lt;g&gt;&lt;rect x="261" y="493" fill="#eaeaea" stroke="#666" width="150" height="65" name="H" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="336" y="525.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="336" dy="0"&gt;herdr&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x="0" y="493" fill="#eaeaea" stroke="#666" width="150" height="65" name="D" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="75" y="525.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;daemon&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor7" x1="336" y1="65" x2="336" y2="493" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="H" data-et="life-line" data-id="H"&gt;&lt;/line&gt;&lt;g id="root-7" data-et="participant" data-type="participant" data-id="H"&gt;&lt;rect x="261" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="H" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="336" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="336" dy="0"&gt;herdr&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor6" x1="75" y1="65" x2="75" y2="493" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="D" data-et="life-line" data-id="D"&gt;&lt;/line&gt;&lt;g id="root-6" data-et="participant" data-type="participant" data-id="D"&gt;&lt;rect x="0" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="D" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="75" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;daemon&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;style&gt;#dcd14d06924d1858alight{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#dcd14d06924d1858alight .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#dcd14d06924d1858alight .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#dcd14d06924d1858alight .error-icon{fill:rgb(255, 255, 255);}#dcd14d06924d1858alight .error-text{fill:#000000;stroke:#000000;}#dcd14d06924d1858alight .edge-thickness-normal{stroke-width:1px;}#dcd14d06924d1858alight .edge-thickness-thick{stroke-width:3.5px;}#dcd14d06924d1858alight .edge-pattern-solid{stroke-dasharray:0;}#dcd14d06924d1858alight .edge-thickness-invisible{stroke-width:0;fill:none;}#dcd14d06924d1858alight .edge-pattern-dashed{stroke-dasharray:3;}#dcd14d06924d1858alight .edge-pattern-dotted{stroke-dasharray:2;}#dcd14d06924d1858alight .marker{fill:rgb(74, 86, 92);stroke:rgb(74, 86, 92);}#dcd14d06924d1858alight .marker.cross{stroke:rgb(74, 86, 92);}#dcd14d06924d1858alight svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#dcd14d06924d1858alight p{margin:0;}#dcd14d06924d1858alight .actor{stroke:rgb(204, 216, 222);fill:rgb(255, 255, 255);stroke-width:1;}#dcd14d06924d1858alight rect.actor.outer-path[data-look="neo"]{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858alight rect.note[data-look="neo"]{stroke:hsl(52.6829268293, 60%, 73.9215686275%);fill:#fff5ad;filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858alight text.actor&amp;gt;tspan{fill:#333;stroke:none;}#dcd14d06924d1858alight .actor-line{stroke:rgb(204, 216, 222);}#dcd14d06924d1858alight .innerArc{stroke-width:1.5;stroke-dasharray:none;}#dcd14d06924d1858alight .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#dcd14d06924d1858alight .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#dcd14d06924d1858alight [id$="-arrowhead"] path{fill:#333;stroke:#333;}#dcd14d06924d1858alight .sequenceNumber{fill:#b5a9a3;}#dcd14d06924d1858alight [id$="-sequencenumber"]{fill:#333;}#dcd14d06924d1858alight [id$="-crosshead"] path{fill:#333;stroke:#333;}#dcd14d06924d1858alight .messageText{fill:#333;stroke:none;}#dcd14d06924d1858alight .labelBox{stroke:rgb(204, 216, 222);fill:rgb(255, 255, 255);filter:none;}#dcd14d06924d1858alight .labelText,#dcd14d06924d1858alight .labelText&amp;gt;tspan{fill:#333;stroke:none;}#dcd14d06924d1858alight .loopText,#dcd14d06924d1858alight .loopText&amp;gt;tspan{fill:#333;stroke:none;}#dcd14d06924d1858alight .sectionTitle,#dcd14d06924d1858alight .sectionTitle&amp;gt;tspan{fill:#333;stroke:none;}#dcd14d06924d1858alight .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:rgb(204, 216, 222);fill:rgb(204, 216, 222);}#dcd14d06924d1858alight .note{stroke:hsl(52.6829268293, 60%, 73.9215686275%);fill:#fff5ad;}#dcd14d06924d1858alight .noteText,#dcd14d06924d1858alight .noteText&amp;gt;tspan{fill:#333;stroke:none;font-weight:normal;}#dcd14d06924d1858alight .activation0{fill:rgb(182, 240, 255);stroke:hsl(192.3287671233, 100%, 75.6862745098%);}#dcd14d06924d1858alight .activation1{fill:rgb(182, 240, 255);stroke:hsl(192.3287671233, 100%, 75.6862745098%);}#dcd14d06924d1858alight .activation2{fill:rgb(182, 240, 255);stroke:hsl(192.3287671233, 100%, 75.6862745098%);}#dcd14d06924d1858alight .actorPopupMenu{position:absolute;}#dcd14d06924d1858alight .actorPopupMenuPanel{position:absolute;fill:rgb(255, 255, 255);box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#dcd14d06924d1858alight .actor-man circle,#dcd14d06924d1858alight line{fill:rgb(255, 255, 255);stroke-width:2px;}#dcd14d06924d1858alight g rect.rect{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));stroke:rgb(204, 216, 222);}#dcd14d06924d1858alight .node .neo-node{stroke:rgb(204, 216, 222);}#dcd14d06924d1858alight [data-look="neo"].node rect,#dcd14d06924d1858alight [data-look="neo"].cluster rect,#dcd14d06924d1858alight [data-look="neo"].node polygon{stroke:url(#dcd14d06924d1858alight-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858alight [data-look="neo"].swimlane.cluster rect{filter:none;}#dcd14d06924d1858alight [data-look="neo"].node path{stroke:url(#dcd14d06924d1858alight-gradient);stroke-width:1px;}#dcd14d06924d1858alight [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858alight [data-look="neo"].node .neo-line path{stroke:rgb(204, 216, 222);filter:none;}#dcd14d06924d1858alight [data-look="neo"].node circle{stroke:url(#dcd14d06924d1858alight-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858alight [data-look="neo"].node circle .state-start{fill:#000000;}#dcd14d06924d1858alight [data-look="neo"].icon-shape .icon{fill:url(#dcd14d06924d1858alight-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858alight [data-look="neo"].icon-shape .icon-neo path{stroke:url(#dcd14d06924d1858alight-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858alight :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;/g&gt;&lt;defs&gt;&lt;symbol id="dcd14d06924d1858alight-computer" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="dcd14d06924d1858alight-database" fill-rule="evenodd" clip-rule="evenodd"&gt;&lt;path transform="scale(.5)" d="M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="dcd14d06924d1858alight-clock" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858alight-arrowhead" refX="7.9" refY="5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M -1 0 L 10 5 L 0 10 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858alight-crosshead" markerWidth="15" markerHeight="8" orient="auto" refX="4" refY="4.5"&gt;&lt;path fill="none" stroke="#000000" stroke-width="1pt" d="M 1,2 L 6,7 M 6,2 L 1,7" style="stroke-dasharray: 0, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858alight-filled-head" refX="15.5" refY="7" markerWidth="20" markerHeight="28" orient="auto"&gt;&lt;path d="M 18,7 L9,13 L14,7 L9,1 Z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858alight-sequencenumber" refX="15" refY="15" markerWidth="60" markerHeight="40" orient="auto"&gt;&lt;circle cx="15" cy="15" r="6"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858alight-solidTopArrowHead" refX="7.9" refY="7.25" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 8 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858alight-solidBottomArrowHead" refX="7.9" refY="0.75" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 0 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858alight-stickTopArrowHead" refX="7.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 7 7" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858alight-stickBottomArrowHead" refX="7.5" refY="0" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 7 L 7 0" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;g data-et="note" data-id="i3"&gt;&lt;rect x="0" y="213" fill="#EDF2AE" stroke="#666" width="150" height="56" class="note"&gt;&lt;/rect&gt;&lt;text x="75" y="218" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75"&gt;buffer everything&lt;/tspan&gt;&lt;/text&gt;&lt;text x="75" y="236" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75"&gt;it delivers&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g data-et="note" data-id="i6"&gt;&lt;rect x="-31.5" y="371" fill="#EDF2AE" stroke="#666" width="213" height="56" class="note"&gt;&lt;/rect&gt;&lt;text x="75" y="376" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75"&gt;replay buffer, drop&lt;/tspan&gt;&lt;/text&gt;&lt;text x="75" y="394" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75"&gt;what snapshot already has&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;text x="204" y="80" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;ping&lt;/text&gt;&lt;line x1="76" y1="111" x2="332" y2="111" class="messageLine0" data-et="message" data-id="i0" data-from="D" data-to="H" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858alight-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="207" y="126" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;protocol 19&lt;/text&gt;&lt;line x1="335" y1="157" x2="79" y2="157" class="messageLine1" data-et="message" data-id="i1" data-from="H" data-to="D" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858alight-arrowhead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;text x="204" y="172" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;events.subscribe&lt;/text&gt;&lt;line x1="76" y1="203" x2="332" y2="203" class="messageLine0" data-et="message" data-id="i2" data-from="D" data-to="H" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858alight-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="204" y="284" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;session.snapshot&lt;/text&gt;&lt;line x1="76" y1="315" x2="332" y2="315" class="messageLine0" data-et="message" data-id="i4" data-from="D" data-to="H" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858alight-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="207" y="330" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;panes + seq numbers&lt;/text&gt;&lt;line x1="335" y1="361" x2="79" y2="361" class="messageLine1" data-et="message" data-id="i5" data-from="H" data-to="D" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858alight-arrowhead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;text x="204" y="442" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;subscribe per-pane status&lt;/text&gt;&lt;line x1="76" y1="473" x2="332" y2="473" class="messageLine0" data-et="message" data-id="i7" data-from="D" data-to="H" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858alight-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/div&gt;&#10; &lt;div class="not-prose diagram diagram-dark"&gt;&lt;svg id="dcd14d06924d1858adark" width="100%" xmlns="http://www.w3.org/2000/svg" style="max-width: 542.5px;" viewBox="-81.5 -10 542.5 579" role="graphics-document document" aria-roledescription="sequence"&gt;&lt;g&gt;&lt;rect x="261" y="493" fill="#eaeaea" stroke="#666" width="150" height="65" name="H" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="336" y="525.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="336" dy="0"&gt;herdr&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;rect x="0" y="493" fill="#eaeaea" stroke="#666" width="150" height="65" name="D" rx="3" ry="3" class="actor actor-bottom"&gt;&lt;/rect&gt;&lt;text x="75" y="525.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;daemon&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor9" x1="336" y1="65" x2="336" y2="493" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="H" data-et="life-line" data-id="H"&gt;&lt;/line&gt;&lt;g id="root-9" data-et="participant" data-type="participant" data-id="H"&gt;&lt;rect x="261" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="H" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="336" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="336" dy="0"&gt;herdr&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;g&gt;&lt;line id="actor8" x1="75" y1="65" x2="75" y2="493" class="actor-line 200" stroke-width="0.5px" stroke="#999" name="D" data-et="life-line" data-id="D"&gt;&lt;/line&gt;&lt;g id="root-8" data-et="participant" data-type="participant" data-id="D"&gt;&lt;rect x="0" y="0" fill="#eaeaea" stroke="#666" width="150" height="65" name="D" rx="3" ry="3" class="actor actor-top"&gt;&lt;/rect&gt;&lt;text x="75" y="32.5" dominant-baseline="central" alignment-baseline="central" class="actor actor-box" style="text-anchor: middle; font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75" dy="0"&gt;daemon&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;/g&gt;&lt;style&gt;#dcd14d06924d1858adark{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;fill:#ccc;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#dcd14d06924d1858adark .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#dcd14d06924d1858adark .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#dcd14d06924d1858adark .error-icon{fill:#a44141;}#dcd14d06924d1858adark .error-text{fill:#ddd;stroke:#ddd;}#dcd14d06924d1858adark .edge-thickness-normal{stroke-width:1px;}#dcd14d06924d1858adark .edge-thickness-thick{stroke-width:3.5px;}#dcd14d06924d1858adark .edge-pattern-solid{stroke-dasharray:0;}#dcd14d06924d1858adark .edge-thickness-invisible{stroke-width:0;fill:none;}#dcd14d06924d1858adark .edge-pattern-dashed{stroke-dasharray:3;}#dcd14d06924d1858adark .edge-pattern-dotted{stroke-dasharray:2;}#dcd14d06924d1858adark .marker{fill:lightgrey;stroke:lightgrey;}#dcd14d06924d1858adark .marker.cross{stroke:lightgrey;}#dcd14d06924d1858adark svg{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,segoe ui,Roboto,helvetica neue,Arial,noto sans,sans-serif;font-size:16px;}#dcd14d06924d1858adark p{margin:0;}#dcd14d06924d1858adark .actor{stroke:#ccc;fill:#1f2020;stroke-width:1;}#dcd14d06924d1858adark rect.actor.outer-path[data-look="neo"]{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858adark rect.note[data-look="neo"]{stroke:hsl(180, 0%, 18.3529411765%);fill:hsl(180, 1.5873015873%, 28.3529411765%);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858adark text.actor&amp;gt;tspan{fill:lightgrey;stroke:none;}#dcd14d06924d1858adark .actor-line{stroke:#ccc;}#dcd14d06924d1858adark .innerArc{stroke-width:1.5;stroke-dasharray:none;}#dcd14d06924d1858adark .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:lightgrey;}#dcd14d06924d1858adark .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:lightgrey;}#dcd14d06924d1858adark [id$="-arrowhead"] path{fill:lightgrey;stroke:lightgrey;}#dcd14d06924d1858adark .sequenceNumber{fill:black;}#dcd14d06924d1858adark [id$="-sequencenumber"]{fill:lightgrey;}#dcd14d06924d1858adark [id$="-crosshead"] path{fill:lightgrey;stroke:lightgrey;}#dcd14d06924d1858adark .messageText{fill:lightgrey;stroke:none;}#dcd14d06924d1858adark .labelBox{stroke:#ccc;fill:#1f2020;filter:none;}#dcd14d06924d1858adark .labelText,#dcd14d06924d1858adark .labelText&amp;gt;tspan{fill:lightgrey;stroke:none;}#dcd14d06924d1858adark .loopText,#dcd14d06924d1858adark .loopText&amp;gt;tspan{fill:lightgrey;stroke:none;}#dcd14d06924d1858adark .sectionTitle,#dcd14d06924d1858adark .sectionTitle&amp;gt;tspan{fill:lightgrey;stroke:none;}#dcd14d06924d1858adark .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:#ccc;fill:#ccc;}#dcd14d06924d1858adark .note{stroke:hsl(180, 0%, 18.3529411765%);fill:hsl(180, 1.5873015873%, 28.3529411765%);}#dcd14d06924d1858adark .noteText,#dcd14d06924d1858adark .noteText&amp;gt;tspan{fill:rgb(183.8476190475, 181.5523809523, 181.5523809523);stroke:none;font-weight:normal;}#dcd14d06924d1858adark .activation0{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:#ccc;}#dcd14d06924d1858adark .activation1{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:#ccc;}#dcd14d06924d1858adark .activation2{fill:hsl(180, 1.5873015873%, 28.3529411765%);stroke:#ccc;}#dcd14d06924d1858adark .actorPopupMenu{position:absolute;}#dcd14d06924d1858adark .actorPopupMenuPanel{position:absolute;fill:#1f2020;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#dcd14d06924d1858adark .actor-man circle,#dcd14d06924d1858adark line{fill:#1f2020;stroke-width:2px;}#dcd14d06924d1858adark g rect.rect{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));stroke:#ccc;}#dcd14d06924d1858adark .node .neo-node{stroke:#ccc;}#dcd14d06924d1858adark [data-look="neo"].node rect,#dcd14d06924d1858adark [data-look="neo"].cluster rect,#dcd14d06924d1858adark [data-look="neo"].node polygon{stroke:url(#dcd14d06924d1858adark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858adark [data-look="neo"].swimlane.cluster rect{filter:none;}#dcd14d06924d1858adark [data-look="neo"].node path{stroke:url(#dcd14d06924d1858adark-gradient);stroke-width:1px;}#dcd14d06924d1858adark [data-look="neo"].node .outer-path{filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858adark [data-look="neo"].node .neo-line path{stroke:#ccc;filter:none;}#dcd14d06924d1858adark [data-look="neo"].node circle{stroke:url(#dcd14d06924d1858adark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858adark [data-look="neo"].node circle .state-start{fill:#000000;}#dcd14d06924d1858adark [data-look="neo"].icon-shape .icon{fill:url(#dcd14d06924d1858adark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858adark [data-look="neo"].icon-shape .icon-neo path{stroke:url(#dcd14d06924d1858adark-gradient);filter:drop-shadow( 1px 2px 2px rgba(185,185,185,1));}#dcd14d06924d1858adark :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}&lt;/style&gt;&lt;g&gt;&lt;/g&gt;&lt;defs&gt;&lt;symbol id="dcd14d06924d1858adark-computer" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M2 2v13h20v-13h-20zm18 11h-16v-9h16v9zm-10.228 6l.466-1h3.524l.467 1h-4.457zm14.228 3h-24l2-6h2.104l-1.33 4h18.45l-1.297-4h2.073l2 6zm-5-10h-14v-7h14v7z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="dcd14d06924d1858adark-database" fill-rule="evenodd" clip-rule="evenodd"&gt;&lt;path transform="scale(.5)" d="M12.258.001l.256.004.255.005.253.008.251.01.249.012.247.015.246.016.242.019.241.02.239.023.236.024.233.027.231.028.229.031.225.032.223.034.22.036.217.038.214.04.211.041.208.043.205.045.201.046.198.048.194.05.191.051.187.053.183.054.18.056.175.057.172.059.168.06.163.061.16.063.155.064.15.066.074.033.073.033.071.034.07.034.069.035.068.035.067.035.066.035.064.036.064.036.062.036.06.036.06.037.058.037.058.037.055.038.055.038.053.038.052.038.051.039.05.039.048.039.047.039.045.04.044.04.043.04.041.04.04.041.039.041.037.041.036.041.034.041.033.042.032.042.03.042.029.042.027.042.026.043.024.043.023.043.021.043.02.043.018.044.017.043.015.044.013.044.012.044.011.045.009.044.007.045.006.045.004.045.002.045.001.045v17l-.001.045-.002.045-.004.045-.006.045-.007.045-.009.044-.011.045-.012.044-.013.044-.015.044-.017.043-.018.044-.02.043-.021.043-.023.043-.024.043-.026.043-.027.042-.029.042-.03.042-.032.042-.033.042-.034.041-.036.041-.037.041-.039.041-.04.041-.041.04-.043.04-.044.04-.045.04-.047.039-.048.039-.05.039-.051.039-.052.038-.053.038-.055.038-.055.038-.058.037-.058.037-.06.037-.06.036-.062.036-.064.036-.064.036-.066.035-.067.035-.068.035-.069.035-.07.034-.071.034-.073.033-.074.033-.15.066-.155.064-.16.063-.163.061-.168.06-.172.059-.175.057-.18.056-.183.054-.187.053-.191.051-.194.05-.198.048-.201.046-.205.045-.208.043-.211.041-.214.04-.217.038-.22.036-.223.034-.225.032-.229.031-.231.028-.233.027-.236.024-.239.023-.241.02-.242.019-.246.016-.247.015-.249.012-.251.01-.253.008-.255.005-.256.004-.258.001-.258-.001-.256-.004-.255-.005-.253-.008-.251-.01-.249-.012-.247-.015-.245-.016-.243-.019-.241-.02-.238-.023-.236-.024-.234-.027-.231-.028-.228-.031-.226-.032-.223-.034-.22-.036-.217-.038-.214-.04-.211-.041-.208-.043-.204-.045-.201-.046-.198-.048-.195-.05-.19-.051-.187-.053-.184-.054-.179-.056-.176-.057-.172-.059-.167-.06-.164-.061-.159-.063-.155-.064-.151-.066-.074-.033-.072-.033-.072-.034-.07-.034-.069-.035-.068-.035-.067-.035-.066-.035-.064-.036-.063-.036-.062-.036-.061-.036-.06-.037-.058-.037-.057-.037-.056-.038-.055-.038-.053-.038-.052-.038-.051-.039-.049-.039-.049-.039-.046-.039-.046-.04-.044-.04-.043-.04-.041-.04-.04-.041-.039-.041-.037-.041-.036-.041-.034-.041-.033-.042-.032-.042-.03-.042-.029-.042-.027-.042-.026-.043-.024-.043-.023-.043-.021-.043-.02-.043-.018-.044-.017-.043-.015-.044-.013-.044-.012-.044-.011-.045-.009-.044-.007-.045-.006-.045-.004-.045-.002-.045-.001-.045v-17l.001-.045.002-.045.004-.045.006-.045.007-.045.009-.044.011-.045.012-.044.013-.044.015-.044.017-.043.018-.044.02-.043.021-.043.023-.043.024-.043.026-.043.027-.042.029-.042.03-.042.032-.042.033-.042.034-.041.036-.041.037-.041.039-.041.04-.041.041-.04.043-.04.044-.04.046-.04.046-.039.049-.039.049-.039.051-.039.052-.038.053-.038.055-.038.056-.038.057-.037.058-.037.06-.037.061-.036.062-.036.063-.036.064-.036.066-.035.067-.035.068-.035.069-.035.07-.034.072-.034.072-.033.074-.033.151-.066.155-.064.159-.063.164-.061.167-.06.172-.059.176-.057.179-.056.184-.054.187-.053.19-.051.195-.05.198-.048.201-.046.204-.045.208-.043.211-.041.214-.04.217-.038.22-.036.223-.034.226-.032.228-.031.231-.028.234-.027.236-.024.238-.023.241-.02.243-.019.245-.016.247-.015.249-.012.251-.01.253-.008.255-.005.256-.004.258-.001.258.001zm-9.258 20.499v.01l.001.021.003.021.004.022.005.021.006.022.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.023.018.024.019.024.021.024.022.025.023.024.024.025.052.049.056.05.061.051.066.051.07.051.075.051.079.052.084.052.088.052.092.052.097.052.102.051.105.052.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.048.144.049.147.047.152.047.155.047.16.045.163.045.167.043.171.043.176.041.178.041.183.039.187.039.19.037.194.035.197.035.202.033.204.031.209.03.212.029.216.027.219.025.222.024.226.021.23.02.233.018.236.016.24.015.243.012.246.01.249.008.253.005.256.004.259.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.021.224-.024.22-.026.216-.027.212-.028.21-.031.205-.031.202-.034.198-.034.194-.036.191-.037.187-.039.183-.04.179-.04.175-.042.172-.043.168-.044.163-.045.16-.046.155-.046.152-.047.148-.048.143-.049.139-.049.136-.05.131-.05.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.053.083-.051.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.05.023-.024.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.023.01-.022.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.127l-.077.055-.08.053-.083.054-.085.053-.087.052-.09.052-.093.051-.095.05-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.045-.118.044-.12.043-.122.042-.124.042-.126.041-.128.04-.13.04-.132.038-.134.038-.135.037-.138.037-.139.035-.142.035-.143.034-.144.033-.147.032-.148.031-.15.03-.151.03-.153.029-.154.027-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.01-.179.008-.179.008-.181.006-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.006-.179-.008-.179-.008-.178-.01-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.027-.153-.029-.151-.03-.15-.03-.148-.031-.146-.032-.145-.033-.143-.034-.141-.035-.14-.035-.137-.037-.136-.037-.134-.038-.132-.038-.13-.04-.128-.04-.126-.041-.124-.042-.122-.042-.12-.044-.117-.043-.116-.045-.113-.045-.112-.046-.109-.047-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.05-.093-.052-.09-.051-.087-.052-.085-.053-.083-.054-.08-.054-.077-.054v4.127zm0-5.654v.011l.001.021.003.021.004.021.005.022.006.022.007.022.009.022.01.022.011.023.012.023.013.023.015.024.016.023.017.024.018.024.019.024.021.024.022.024.023.025.024.024.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.052.11.051.114.051.119.052.123.05.127.051.131.05.135.049.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.044.171.042.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.022.23.02.233.018.236.016.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.012.241-.015.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.048.139-.05.136-.049.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.051.051-.049.023-.025.023-.024.021-.025.02-.024.019-.024.018-.024.017-.024.015-.023.014-.023.013-.024.012-.022.01-.023.01-.023.008-.022.006-.022.006-.022.004-.021.004-.022.001-.021.001-.021v-4.139l-.077.054-.08.054-.083.054-.085.052-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.049-.105.048-.106.047-.109.047-.111.046-.114.045-.115.044-.118.044-.12.044-.122.042-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.035-.143.033-.144.033-.147.033-.148.031-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.025-.161.024-.162.023-.163.022-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.011-.178.009-.179.009-.179.007-.181.007-.182.005-.182.004-.184.003-.184.002h-.37l-.184-.002-.184-.003-.182-.004-.182-.005-.181-.007-.179-.007-.179-.009-.178-.009-.176-.011-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.022-.162-.023-.161-.024-.159-.025-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.031-.146-.033-.145-.033-.143-.033-.141-.035-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.04-.126-.041-.124-.042-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.047-.105-.048-.102-.049-.1-.049-.097-.05-.095-.051-.093-.051-.09-.051-.087-.053-.085-.052-.083-.054-.08-.054-.077-.054v4.139zm0-5.666v.011l.001.02.003.022.004.021.005.022.006.021.007.022.009.023.01.022.011.023.012.023.013.023.015.023.016.024.017.024.018.023.019.024.021.025.022.024.023.024.024.025.052.05.056.05.061.05.066.051.07.051.075.052.079.051.084.052.088.052.092.052.097.052.102.052.105.051.11.052.114.051.119.051.123.051.127.05.131.05.135.05.139.049.144.048.147.048.152.047.155.046.16.045.163.045.167.043.171.043.176.042.178.04.183.04.187.038.19.037.194.036.197.034.202.033.204.032.209.03.212.028.216.027.219.025.222.024.226.021.23.02.233.018.236.017.24.014.243.012.246.01.249.008.253.006.256.003.259.001.26-.001.257-.003.254-.006.25-.008.247-.01.244-.013.241-.014.237-.016.233-.018.231-.02.226-.022.224-.024.22-.025.216-.027.212-.029.21-.03.205-.032.202-.033.198-.035.194-.036.191-.037.187-.039.183-.039.179-.041.175-.042.172-.043.168-.044.163-.045.16-.045.155-.047.152-.047.148-.048.143-.049.139-.049.136-.049.131-.051.126-.05.123-.051.118-.052.114-.051.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.052.07-.051.065-.051.06-.051.056-.05.051-.049.023-.025.023-.025.021-.024.02-.024.019-.024.018-.024.017-.024.015-.023.014-.024.013-.023.012-.023.01-.022.01-.023.008-.022.006-.022.006-.022.004-.022.004-.021.001-.021.001-.021v-4.153l-.077.054-.08.054-.083.053-.085.053-.087.053-.09.051-.093.051-.095.051-.097.05-.1.049-.102.048-.105.048-.106.048-.109.046-.111.046-.114.046-.115.044-.118.044-.12.043-.122.043-.124.042-.126.041-.128.04-.13.039-.132.039-.134.038-.135.037-.138.036-.139.036-.142.034-.143.034-.144.033-.147.032-.148.032-.15.03-.151.03-.153.028-.154.028-.156.027-.158.026-.159.024-.161.024-.162.023-.163.023-.165.021-.166.02-.167.019-.169.018-.169.017-.171.016-.173.015-.173.014-.175.013-.175.012-.177.01-.178.01-.179.009-.179.007-.181.006-.182.006-.182.004-.184.003-.184.001-.185.001-.185-.001-.184-.001-.184-.003-.182-.004-.182-.006-.181-.006-.179-.007-.179-.009-.178-.01-.176-.01-.176-.012-.175-.013-.173-.014-.172-.015-.171-.016-.17-.017-.169-.018-.167-.019-.166-.02-.165-.021-.163-.023-.162-.023-.161-.024-.159-.024-.157-.026-.156-.027-.155-.028-.153-.028-.151-.03-.15-.03-.148-.032-.146-.032-.145-.033-.143-.034-.141-.034-.14-.036-.137-.036-.136-.037-.134-.038-.132-.039-.13-.039-.128-.041-.126-.041-.124-.041-.122-.043-.12-.043-.117-.044-.116-.044-.113-.046-.112-.046-.109-.046-.106-.048-.105-.048-.102-.048-.1-.05-.097-.049-.095-.051-.093-.051-.09-.052-.087-.052-.085-.053-.083-.053-.08-.054-.077-.054v4.153zm8.74-8.179l-.257.004-.254.005-.25.008-.247.011-.244.012-.241.014-.237.016-.233.018-.231.021-.226.022-.224.023-.22.026-.216.027-.212.028-.21.031-.205.032-.202.033-.198.034-.194.036-.191.038-.187.038-.183.04-.179.041-.175.042-.172.043-.168.043-.163.045-.16.046-.155.046-.152.048-.148.048-.143.048-.139.049-.136.05-.131.05-.126.051-.123.051-.118.051-.114.052-.11.052-.106.052-.101.052-.096.052-.092.052-.088.052-.083.052-.079.052-.074.051-.07.052-.065.051-.06.05-.056.05-.051.05-.023.025-.023.024-.021.024-.02.025-.019.024-.018.024-.017.023-.015.024-.014.023-.013.023-.012.023-.01.023-.01.022-.008.022-.006.023-.006.021-.004.022-.004.021-.001.021-.001.021.001.021.001.021.004.021.004.022.006.021.006.023.008.022.01.022.01.023.012.023.013.023.014.023.015.024.017.023.018.024.019.024.02.025.021.024.023.024.023.025.051.05.056.05.06.05.065.051.07.052.074.051.079.052.083.052.088.052.092.052.096.052.101.052.106.052.11.052.114.052.118.051.123.051.126.051.131.05.136.05.139.049.143.048.148.048.152.048.155.046.16.046.163.045.168.043.172.043.175.042.179.041.183.04.187.038.191.038.194.036.198.034.202.033.205.032.21.031.212.028.216.027.22.026.224.023.226.022.231.021.233.018.237.016.241.014.244.012.247.011.25.008.254.005.257.004.26.001.26-.001.257-.004.254-.005.25-.008.247-.011.244-.012.241-.014.237-.016.233-.018.231-.021.226-.022.224-.023.22-.026.216-.027.212-.028.21-.031.205-.032.202-.033.198-.034.194-.036.191-.038.187-.038.183-.04.179-.041.175-.042.172-.043.168-.043.163-.045.16-.046.155-.046.152-.048.148-.048.143-.048.139-.049.136-.05.131-.05.126-.051.123-.051.118-.051.114-.052.11-.052.106-.052.101-.052.096-.052.092-.052.088-.052.083-.052.079-.052.074-.051.07-.052.065-.051.06-.05.056-.05.051-.05.023-.025.023-.024.021-.024.02-.025.019-.024.018-.024.017-.023.015-.024.014-.023.013-.023.012-.023.01-.023.01-.022.008-.022.006-.023.006-.021.004-.022.004-.021.001-.021.001-.021-.001-.021-.001-.021-.004-.021-.004-.022-.006-.021-.006-.023-.008-.022-.01-.022-.01-.023-.012-.023-.013-.023-.014-.023-.015-.024-.017-.023-.018-.024-.019-.024-.02-.025-.021-.024-.023-.024-.023-.025-.051-.05-.056-.05-.06-.05-.065-.051-.07-.052-.074-.051-.079-.052-.083-.052-.088-.052-.092-.052-.096-.052-.101-.052-.106-.052-.11-.052-.114-.052-.118-.051-.123-.051-.126-.051-.131-.05-.136-.05-.139-.049-.143-.048-.148-.048-.152-.048-.155-.046-.16-.046-.163-.045-.168-.043-.172-.043-.175-.042-.179-.041-.183-.04-.187-.038-.191-.038-.194-.036-.198-.034-.202-.033-.205-.032-.21-.031-.212-.028-.216-.027-.22-.026-.224-.023-.226-.022-.231-.021-.233-.018-.237-.016-.241-.014-.244-.012-.247-.011-.25-.008-.254-.005-.257-.004-.26-.001-.26.001z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;symbol id="dcd14d06924d1858adark-clock" width="24" height="24"&gt;&lt;path transform="scale(.5)" d="M12 2c5.514 0 10 4.486 10 10s-4.486 10-10 10-10-4.486-10-10 4.486-10 10-10zm0-2c-6.627 0-12 5.373-12 12s5.373 12 12 12 12-5.373 12-12-5.373-12-12-12zm5.848 12.459c.202.038.202.333.001.372-1.907.361-6.045 1.111-6.547 1.111-.719 0-1.301-.582-1.301-1.301 0-.512.77-5.447 1.125-7.445.034-.192.312-.181.343.014l.985 6.238 5.394 1.011z"&gt;&lt;/path&gt;&lt;/symbol&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858adark-arrowhead" refX="7.9" refY="5" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M -1 0 L 10 5 L 0 10 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858adark-crosshead" markerWidth="15" markerHeight="8" orient="auto" refX="4" refY="4.5"&gt;&lt;path fill="none" stroke="#000000" stroke-width="1pt" d="M 1,2 L 6,7 M 6,2 L 1,7" style="stroke-dasharray: 0, 0;"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858adark-filled-head" refX="15.5" refY="7" markerWidth="20" markerHeight="28" orient="auto"&gt;&lt;path d="M 18,7 L9,13 L14,7 L9,1 Z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858adark-sequencenumber" refX="15" refY="15" markerWidth="60" markerHeight="40" orient="auto"&gt;&lt;circle cx="15" cy="15" r="6"&gt;&lt;/circle&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858adark-solidTopArrowHead" refX="7.9" refY="7.25" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 8 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858adark-solidBottomArrowHead" refX="7.9" refY="0.75" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 10 0 L 0 8 z"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858adark-stickTopArrowHead" refX="7.5" refY="7" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 0 L 7 7" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;defs&gt;&lt;marker id="dcd14d06924d1858adark-stickBottomArrowHead" refX="7.5" refY="0" markerUnits="userSpaceOnUse" markerWidth="12" markerHeight="12" orient="auto-start-reverse"&gt;&lt;path d="M 0 7 L 7 0" stroke="black" stroke-width="1.5" fill="none"&gt;&lt;/path&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;g data-et="note" data-id="i3"&gt;&lt;rect x="0" y="213" fill="#EDF2AE" stroke="#666" width="150" height="56" class="note"&gt;&lt;/rect&gt;&lt;text x="75" y="218" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75"&gt;buffer everything&lt;/tspan&gt;&lt;/text&gt;&lt;text x="75" y="236" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75"&gt;it delivers&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;g data-et="note" data-id="i6"&gt;&lt;rect x="-31.5" y="371" fill="#EDF2AE" stroke="#666" width="213" height="56" class="note"&gt;&lt;/rect&gt;&lt;text x="75" y="376" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75"&gt;replay buffer, drop&lt;/tspan&gt;&lt;/text&gt;&lt;text x="75" y="394" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="noteText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;&lt;tspan x="75"&gt;what snapshot already has&lt;/tspan&gt;&lt;/text&gt;&lt;/g&gt;&lt;text x="204" y="80" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;ping&lt;/text&gt;&lt;line x1="76" y1="111" x2="332" y2="111" class="messageLine0" data-et="message" data-id="i0" data-from="D" data-to="H" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858adark-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="207" y="126" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;protocol 19&lt;/text&gt;&lt;line x1="335" y1="157" x2="79" y2="157" class="messageLine1" data-et="message" data-id="i1" data-from="H" data-to="D" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858adark-arrowhead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;text x="204" y="172" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;events.subscribe&lt;/text&gt;&lt;line x1="76" y1="203" x2="332" y2="203" class="messageLine0" data-et="message" data-id="i2" data-from="D" data-to="H" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858adark-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="204" y="284" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;session.snapshot&lt;/text&gt;&lt;line x1="76" y1="315" x2="332" y2="315" class="messageLine0" data-et="message" data-id="i4" data-from="D" data-to="H" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858adark-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;text x="207" y="330" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;panes + seq numbers&lt;/text&gt;&lt;line x1="335" y1="361" x2="79" y2="361" class="messageLine1" data-et="message" data-id="i5" data-from="H" data-to="D" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858adark-arrowhead)" style="stroke-dasharray: 3, 3; fill: none;"&gt;&lt;/line&gt;&lt;text x="204" y="442" text-anchor="middle" dominant-baseline="middle" alignment-baseline="middle" class="messageText" dy="1em" style="font-size: 16px; font-weight: 400;"&gt;subscribe per-pane status&lt;/text&gt;&lt;line x1="76" y1="473" x2="332" y2="473" class="messageLine0" data-et="message" data-id="i7" data-from="D" data-to="H" stroke-width="2" stroke="none" marker-end="url(#dcd14d06924d1858adark-arrowhead)" style="fill: none;"&gt;&lt;/line&gt;&lt;/svg&gt;&lt;/div&gt;&#10;&lt;p&gt;Buffered events are either already reflected in the snapshot or strictly newer than it, and &lt;code&gt;state_change_seq&lt;/code&gt; tells you which. Replayed events carrying no sequence number get dropped whenever the pane is already cached, and applied only when they describe something the snapshot missed, such as a pane created after it was taken.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;When the recovery mechanism prevents recovery&#10; &lt;div id="when-the-recovery-mechanism-prevents-recovery" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#when-the-recovery-mechanism-prevents-recovery" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;On a busy session the daemon reconnected 24 times in 12 seconds.&lt;/p&gt;&#10;&lt;p&gt;The cause is the all-or-nothing subscription rule meeting reality. Short-lived panes, popups and plugin panes, routinely vanish between their &lt;code&gt;pane_created&lt;/code&gt; event and the resubscribe that event triggers. herdr fails the entire subscribe batch when one &lt;code&gt;pane_id&lt;/code&gt; in it is stale. Tearing down the connection on that failure and reconnecting rebuilds the identical doomed batch, which fails identically, which tears down the connection.&lt;/p&gt;&#10;&lt;p&gt;An endless &lt;code&gt;online&lt;/code&gt; to &lt;code&gt;connecting&lt;/code&gt; flap, in which the recovery mechanism was the only thing preventing recovery.&lt;/p&gt;&#10;&lt;p&gt;The fix prunes the vanished pane out of the cache and retries, bounded:&lt;/p&gt;&#10;&lt;div class="highlight-wrapper"&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-ts" data-lang="ts"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/** Bound on per-open prune retries, so a pathological server can&amp;#39;t spin&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt; * this loop forever. */&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kr"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MAX_STALE_PANE_PRUNES&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nx"&gt;stalePaneId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;: &lt;span class="kt"&gt;unknown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nx"&gt;HerdrApiError&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;pane_not_found&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sr"&gt;/pane (\S+) not found/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;.[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&#10;&lt;p&gt;Reconnects went from 24 in 12 seconds to one.&lt;/p&gt;&#10;&lt;p&gt;The bound matters more than the prune. Retry-until-it-works against a server that will never agree is the same flap with extra steps, so the loop gives up after eight prunes and throws with a message naming the count. A recovery path that cannot fail is not a recovery path.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;What it came to&#10; &lt;div id="what-it-came-to" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#what-it-came-to" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;p&gt;Four days, 36 commits on &lt;code&gt;main&lt;/code&gt;, 35 of them merged through pull requests behind seven required checks. Four packages, 504 tests across 32 files, and close to one line of test for every line of source, which was not a target so much as a description of where the difficulty lived.&lt;/p&gt;&#10;&lt;p&gt;The difficulty did not live in the Stream Deck. It lived in the seam.&lt;/p&gt;&#10;&#10;&lt;h2 class="relative group"&gt;Lessons&#10; &lt;div id="lessons" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#lessons" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Changing the substrate is a deletion strategy. If a dependency already tracks the thing you are inferring, the win is measured in subsystems removed, not features added.&lt;/li&gt;&#10;&lt;li&gt;Ask what a protocol costs before you claim it saved you something. Event naming, connection lifetime and subscription granularity are the parts no schema documents and the parts you will actually pay for.&lt;/li&gt;&#10;&lt;li&gt;Subscribe first and buffer, then snapshot, then replay against the sequence numbers. Snapshot-then-subscribe drops transitions and subscribe-then-snapshot double counts.&lt;/li&gt;&#10;&lt;li&gt;If one bad id fails a whole batch, reconnecting rebuilds the same bad batch. Prune the offending item and retry, bounded, or the recovery path becomes the outage.&lt;/li&gt;&#10;&lt;li&gt;Building on a tool you are still evaluating makes the evaluation harder to fail. Write down that you did it, because sunk cost will not remind you later.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 class="relative group"&gt;References&#10; &lt;div id="references" class="anchor"&gt;&lt;/div&gt;&#10; &#10; &lt;span&#10; class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none"&gt;&#10; &lt;a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#references" aria-label="Anchor"&gt;#&lt;/a&gt;&#10; &lt;/span&gt;&#10; &#10;&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;herdr: &lt;a href="https://herdr.dev" target="_blank" rel="noreferrer"&gt;https://herdr.dev&lt;/a&gt; (source: &lt;a href="https://github.com/herdrdev/herdr" target="_blank" rel="noreferrer"&gt;https://github.com/herdrdev/herdr&lt;/a&gt;)&lt;/li&gt;&#10;&lt;li&gt;HerdDeck: &lt;a href="https://github.com/nickboy/herddeck" target="_blank" rel="noreferrer"&gt;https://github.com/nickboy/herddeck&lt;/a&gt;. Engineering notes, including the protocol behaviour above, in &lt;a href="https://github.com/nickboy/herddeck/blob/main/docs/engineering-notes.md" target="_blank" rel="noreferrer"&gt;&lt;code&gt;docs/engineering-notes.md&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;ClaudeDeck, the predecessor: &lt;a href="https://github.com/nickboy/claudedeck" target="_blank" rel="noreferrer"&gt;https://github.com/nickboy/claudedeck&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The herdr trial this rebuild jumped ahead of: &lt;a href="https://nick-liu.com/posts/herdr-trial-agent-multiplexer/" &gt;Running six agents made tab patrol my biggest time sink. So: a herdr trial.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The hook layer HerdDeck deleted: &lt;a href="https://nick-liu.com/posts/claude-code-hooks-reality/" &gt;The Claude Code hooks docs are wrong. Here&amp;rsquo;s what&amp;rsquo;s actually on the wire.&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The PTY runner HerdDeck deleted: &lt;a href="https://nick-liu.com/posts/pty-wrap-migration/" &gt;I split my daemon in two so a Node subprocess could own the PTY&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;The codesigning and TCC path HerdDeck deleted: &lt;a href="https://nick-liu.com/posts/tcc-cdhash-trap/" &gt;TCC pins your Accessibility grant to a cdhash. Every rebuild breaks it.&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</content:encoded></item></channel></rss>