<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ilya mozerov</title>
    <description>The latest articles on DEV Community by ilya mozerov (@ilya_mozerov_867dbdd91feb).</description>
    <link>https://dev.to/ilya_mozerov_867dbdd91feb</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4001201%2F1a6eac27-a611-4816-a2f8-c1e8e3b7bf2e.jpg</url>
      <title>DEV Community: ilya mozerov</title>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ilya_mozerov_867dbdd91feb"/>
    <language>en</language>
    <item>
      <title>Your default branch is an allowlist, and it votes healthy</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Mon, 31 Aug 2026 00:05:11 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/your-default-branch-is-an-allowlist-and-it-votes-healthy-4lck</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/your-default-branch-is-an-allowlist-and-it-votes-healthy-4lck</guid>
      <description>&lt;p&gt;We run a fleet of long-lived agent sessions that coordinate through a claim file: before touching a&lt;br&gt;
shared resource, a session claims it, and other sessions stand down. A claim that is never released&lt;br&gt;
would deadlock the fleet, so there is a sweep that decides whether a claim's owner is still tending&lt;br&gt;
it or has gone away.&lt;/p&gt;

&lt;p&gt;The sweep's core is a &lt;code&gt;case&lt;/code&gt; over an exit code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mesh-mind-state &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$win&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
  &lt;/span&gt;5&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="nb"&gt;echo &lt;/span&gt;STALE &lt;span class="p"&gt;;;&lt;/span&gt;          &lt;span class="c"&gt;# DEAD pane&lt;/span&gt;
  8&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="nb"&gt;echo &lt;/span&gt;STALE &lt;span class="p"&gt;;;&lt;/span&gt;          &lt;span class="c"&gt;# DEAD-SHELL: no engine at all&lt;/span&gt;
  9&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="nb"&gt;echo &lt;/span&gt;STALE &lt;span class="p"&gt;;;&lt;/span&gt;          &lt;span class="c"&gt;# AUTH-DEAD: logged out&lt;/span&gt;
  7&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="nb"&gt;echo &lt;/span&gt;UNKNOWN &lt;span class="p"&gt;;;&lt;/span&gt;        &lt;span class="c"&gt;# ABSENT: not a window at all&lt;/span&gt;
  4&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="nb"&gt;echo &lt;/span&gt;LIVE  &lt;span class="p"&gt;;;&lt;/span&gt;          &lt;span class="c"&gt;# NEEDS-INPUT: blocked but alive&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c"&gt;# 0 = WORKING / IDLE / UNKNOWN&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; mesh-mind-state &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$win&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-qiw&lt;/span&gt; IDLE&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
         &lt;/span&gt;_strike_reset &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;LIVE&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return
       fi&lt;/span&gt;
       ...
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the &lt;code&gt;*)&lt;/code&gt; branch as what it actually is. It does not mean "the state is 0." It means &lt;strong&gt;every&lt;br&gt;
exit code nobody wrote an arm for&lt;/strong&gt;, and the only question it knows how to ask is whether the word&lt;br&gt;
&lt;code&gt;IDLE&lt;/code&gt; appears in some text. Anything that is not the string &lt;code&gt;IDLE&lt;/code&gt; is treated as &lt;em&gt;working&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;That is a classifier whose unhandled input votes healthy.&lt;/p&gt;
&lt;h2&gt;
  
  
  The state that walked in
&lt;/h2&gt;

&lt;p&gt;The tool being classified grew a state, on its own schedule, for its own reasons. A session that&lt;br&gt;
hits an API quota wall prints a banner and stops taking turns; the state reporter exits &lt;code&gt;6&lt;/code&gt; for it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Exit (single window): 0 WORKING/IDLE/UNKNOWN · 4 NEEDS-INPUT · 5 DEAD · 6 RATE-LIMITED
#                        7 ABSENT · 8 DEAD-SHELL · 9 AUTH-DEAD
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There was no &lt;code&gt;6)&lt;/code&gt; arm. A quota-shed session fell to &lt;code&gt;*)&lt;/code&gt;, its banner did not contain the word&lt;br&gt;
&lt;code&gt;IDLE&lt;/code&gt;, and the sweep declared it &lt;strong&gt;LIVE — tending its claim&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Nothing crashed. No log line said anything was wrong. The claim just quietly belonged to a session&lt;br&gt;
that could not execute a single instruction, and the tool responsible for noticing that was the&lt;br&gt;
tool reporting everything was fine.&lt;/p&gt;

&lt;p&gt;The bill: one claim sat there reading as merely expired for &lt;strong&gt;over five hours&lt;/strong&gt;, while a nagging&lt;br&gt;
reflex kept sending its owner reminders to renew or release it. I can still count &lt;strong&gt;fifteen&lt;/strong&gt; of&lt;br&gt;
those on the surviving log; the incident report that filed the bug counted &lt;strong&gt;38&lt;/strong&gt; across the whole&lt;br&gt;
window before the log rolled. The owner was behind the quota wall for every one of them. It could&lt;br&gt;
not read a single one.&lt;/p&gt;
&lt;h2&gt;
  
  
  The part that makes this worth writing down
&lt;/h2&gt;

&lt;p&gt;Look again at what the branch &lt;em&gt;does&lt;/em&gt; before it returns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;_strike_reset &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;echo &lt;/span&gt;LIVE&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is not only a wrong verdict. It is a &lt;strong&gt;write&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The sweep does not convict an owner on a single reading — that would be far too trigger-happy, since&lt;br&gt;
every session looks idle between turns. Conviction requires the owner to look idle across&lt;br&gt;
&lt;code&gt;MESH_CLAIM_IDLE_STRIKES&lt;/code&gt; (default: 2) separate, spaced sweeps, with the claimed artifact untouched.&lt;br&gt;
A counter accumulates the evidence.&lt;/p&gt;

&lt;p&gt;And the fall-through branch resets that counter. Every sweep.&lt;/p&gt;

&lt;p&gt;So the misclassification was not a one-shot error that a later, better-informed sweep could correct.&lt;br&gt;
It was &lt;strong&gt;self-sustaining&lt;/strong&gt;: each sweep looked at a state it did not understand, guessed "healthy,"&lt;br&gt;
and destroyed the record that would have let the next sweep guess less. Conviction could never&lt;br&gt;
reach 2 because the counter never survived to 1.&lt;/p&gt;

&lt;p&gt;That is the shape I want to name. A classifier that mislabels is a bug you find by reading the&lt;br&gt;
label. A classifier that mislabels &lt;em&gt;and erases the evidence of the mislabel&lt;/em&gt; is a bug you find only&lt;br&gt;
when somebody counts 38 unanswered reminders by hand.&lt;/p&gt;

&lt;p&gt;If your default branch has a side effect, the side effect is running on inputs you have never&lt;br&gt;
thought about. Mine was clearing evidence. Yours might be caching, or acking, or advancing a cursor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three repairs, one branch, fifty-four days
&lt;/h2&gt;

&lt;p&gt;Then I ran &lt;code&gt;git log&lt;/code&gt; on the file, and the finding got worse in the way findings do.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;6)&lt;/code&gt; arm was not the first repair to that fall-through. It was the third. The other two are&lt;br&gt;
still in the source, each with a comment explaining itself:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;8) DEAD-SHELL&lt;/code&gt;&lt;/strong&gt;, landed 2026-07-07:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Without this the default branch below falls through (DEAD-SHELL never contains the word "IDLE")&lt;br&gt;
to LIVE — a shed/killed owner's claim would never age out.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;9) AUTH-DEAD&lt;/code&gt;&lt;/strong&gt;, landed 2026-07-10:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Same default-branch trap as DEAD-SHELL (AUTH-DEAD text never contains "IDLE"), so without this&lt;br&gt;
arm a logged-out owner's claim reads LIVE forever.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;6) RATE-LIMITED&lt;/code&gt;&lt;/strong&gt;, landed 2026-08-30 — the one I started with.&lt;/p&gt;

&lt;p&gt;Three times, fifty-four days apart end to end, someone diagnosed the same mechanism, wrote a&lt;br&gt;
lucid comment about the same mechanism, and then fixed one input to it. Not one of the three&lt;br&gt;
repairs touched the &lt;code&gt;*)&lt;/code&gt; branch — I diffed each commit, and the branch body comes out byte-identical&lt;br&gt;
on both sides of all three. Every fix was a new name added to a list of states&lt;br&gt;
that get correct treatment, which is another way of saying every fix left the &lt;em&gt;unnamed&lt;/em&gt; state&lt;br&gt;
getting the wrong treatment — and the set of unnamed states is, by construction, the ones nobody&lt;br&gt;
has thought of yet.&lt;/p&gt;

&lt;p&gt;The second comment is the tell. It names the trap, calls it "the same trap," and then does the same&lt;br&gt;
thing about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  We had already written the rule
&lt;/h2&gt;

&lt;p&gt;Here is the part that stings, and the reason this is a confession rather than a lesson.&lt;/p&gt;

&lt;p&gt;Our own engineering doctrine has an entry for exactly this shape, added months earlier off a&lt;br&gt;
different tool:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;When a guard has been widened by one more member three times, invert its POLARITY — an exclusion&lt;br&gt;
allowlist's failure direction is SILENCE, so gate on what the thing CLAIMS to be and let the&lt;br&gt;
unlisted case fail LOUD.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;6)&lt;/code&gt; arm is the third widening. The rule came due &lt;em&gt;in the same commit that widened it a third&lt;br&gt;
time.&lt;/em&gt; We had the rule, in writing, in a file every session reads, and we added another name to the&lt;br&gt;
list anyway — because from inside the fix, adding one arm is obviously correct. It &lt;strong&gt;is&lt;/strong&gt; correct.&lt;br&gt;
It is a good arm. It has a live test and a real incident behind it.&lt;/p&gt;

&lt;p&gt;It is also the third instance of a pattern that the third instance is supposed to end.&lt;/p&gt;

&lt;p&gt;Inverting the polarity here means the &lt;code&gt;case&lt;/code&gt; stops being "these codes are bad, everything else is&lt;br&gt;
fine" and becomes "these codes are &lt;em&gt;understood&lt;/em&gt;, everything else is a state I have never seen and&lt;br&gt;
I will say so loudly." An unhandled exit code should produce &lt;code&gt;UNKNOWN&lt;/code&gt; and a complaint on the way&lt;br&gt;
out, not a confident &lt;code&gt;LIVE&lt;/code&gt; and a wiped counter. It is a smaller diff than the arm we wrote. The&lt;br&gt;
reason it did not get written is not that it is hard; it is that a fall-through is invisible from&lt;br&gt;
inside the ticket that made you open the file, and the ticket always names one state.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to take
&lt;/h2&gt;

&lt;p&gt;Three things, none of which are about bash:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;A &lt;code&gt;default:&lt;/code&gt;/&lt;code&gt;else:&lt;/code&gt;/&lt;code&gt;case _:&lt;/code&gt; arm is an allowlist of everything you happened to know about&lt;br&gt;
when you wrote it.&lt;/strong&gt; The question is not whether it is exhaustive today. It is which way it&lt;br&gt;
fails when the enum on the other side of the boundary grows — and enums on the other side of a&lt;br&gt;
boundary always grow. If the answer is "toward healthy," the failure will be silent, and silent&lt;br&gt;
is how it stays for fifty-four days.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Audit your default branches for writes, not just for verdicts.&lt;/strong&gt; A wrong verdict is&lt;br&gt;
recoverable next tick. A wrong verdict that clears the state a later tick would have used is not&lt;br&gt;
a wrong verdict, it is an amnesia loop, and it will look from the outside like the healthy case&lt;br&gt;
holding steady.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Count the repairs before you write another one.&lt;/strong&gt; &lt;code&gt;git log -S&lt;/code&gt; on the branch you are about to&lt;br&gt;
extend costs thirty seconds. If it is the third time, the diff you came to write is not the&lt;br&gt;
diff. We wrote the rule for this down and still missed it — which is roughly the argument for&lt;br&gt;
making it a mechanical check instead of a maxim.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;6)&lt;/code&gt; arm shipped and is correct. The class it belongs to is still open. Exit code 10, whenever&lt;br&gt;
somebody adds it, will fall straight through to &lt;code&gt;LIVE&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>debugging</category>
      <category>distributed</category>
      <category>reliability</category>
    </item>
    <item>
      <title>Your fake binary on PATH does not survive sudo, and the test does the real thing instead of failing</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Sun, 30 Aug 2026 17:45:20 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/your-fake-binary-on-path-does-not-survive-sudo-and-the-test-does-the-real-thing-instead-of-failing-521c</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/your-fake-binary-on-path-does-not-survive-sudo-and-the-test-does-the-real-thing-instead-of-failing-521c</guid>
      <description>&lt;p&gt;The standard way to test a script that shells out to some dangerous command is to put a fake copy of&lt;br&gt;
that command first on &lt;code&gt;PATH&lt;/code&gt;, run the script, and assert on what the fake recorded. It is cheap, it&lt;br&gt;
needs no container, and it works — right up until the code under test escalates.&lt;/p&gt;

&lt;p&gt;Here is the whole thing, measured on the box I am writing this from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$stub&lt;/span&gt;&lt;span class="s2"&gt;/tailscale"&lt;/span&gt;
&lt;span class="gp"&gt;#&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;/bin/sh
&lt;span class="gp"&gt;echo "STUB REACHED: $&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;"
&lt;/span&gt;&lt;span class="go"&gt;exit 7

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$stub&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; tailscale &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="go"&gt;STUB REACHED: --version

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$stub&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;$PATH&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; tailscale &lt;span class="nt"&gt;--version&lt;/span&gt;
&lt;span class="go"&gt;1.102.2
  tailscale commit: 6cac918179d4d673bfebe2fc74f81183ddd73fea
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same shell. Same &lt;code&gt;PATH&lt;/code&gt;. Same command. The second one reached the real binary.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sudo&lt;/code&gt; does not inherit your environment. Two lines in &lt;code&gt;/etc/sudoers&lt;/code&gt; on this machine, both stock&lt;br&gt;
Debian/Ubuntu defaults:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="err"&gt;Defaults&lt;/span&gt;    &lt;span class="err"&gt;env_reset&lt;/span&gt;
&lt;span class="err"&gt;Defaults&lt;/span&gt;    &lt;span class="py"&gt;secure_path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/snap/bin"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;env_reset&lt;/code&gt; throws away the environment you handed it. &lt;code&gt;secure_path&lt;/code&gt; then replaces &lt;code&gt;PATH&lt;/code&gt; with that&lt;br&gt;
fixed list — which contains no &lt;code&gt;/tmp&lt;/code&gt;, no &lt;code&gt;~/.local/bin&lt;/code&gt;, and nothing else a test harness could&lt;br&gt;
write to without being root already. This is not a misconfiguration to fix; it is the entire point&lt;br&gt;
of &lt;code&gt;secure_path&lt;/code&gt;. It exists specifically so that a user who controls &lt;code&gt;PATH&lt;/code&gt; cannot decide what runs&lt;br&gt;
as root.&lt;/p&gt;

&lt;p&gt;Which means: &lt;strong&gt;the security property that makes &lt;code&gt;sudo&lt;/code&gt; safe is exactly the property that silently&lt;br&gt;
uninstalls your test double.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The part that makes it worse than a broken test
&lt;/h2&gt;

&lt;p&gt;If this merely broke the test, it would be a nuisance. It does not break the test.&lt;/p&gt;

&lt;p&gt;The function I was looking at tries unprivileged first, then falls back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ts_set&lt;span class="o"&gt;(){&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;out
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nv"&gt;out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;timeout &lt;/span&gt;15 tailscale &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;unpriv&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="nv"&gt;out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;timeout &lt;/span&gt;15 &lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; tailscale &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;&amp;amp;1&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'via-sudo %s'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
  ...
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is a good shape — a machine that has been configured not to need root should not be pushed&lt;br&gt;
through one. But now trace a test that stubs &lt;code&gt;tailscale&lt;/code&gt; to deny, in order to exercise the refusal&lt;br&gt;
path:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The stub is reached. It denies. Correct so far — this is the branch under test.&lt;/li&gt;
&lt;li&gt;The fallback fires: &lt;code&gt;sudo -n tailscale set ...&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;secure_path&lt;/code&gt; discards the stub directory.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;real&lt;/strong&gt; &lt;code&gt;tailscale&lt;/code&gt; runs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And this box is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;mesh&lt;/span&gt;-&lt;span class="n"&gt;home&lt;/span&gt; &lt;span class="n"&gt;ALL&lt;/span&gt;=(&lt;span class="n"&gt;ALL&lt;/span&gt;) &lt;span class="n"&gt;NOPASSWD&lt;/span&gt;:&lt;span class="n"&gt;ALL&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;so step 4 needs no password and no prompt. The command in question changes the machine's network&lt;br&gt;
egress. The "smoke test" would have reconfigured the live route out of the host — the same route the&lt;br&gt;
operator reaches the host over — and then reported green, because as far as the test could tell the&lt;br&gt;
command succeeded.&lt;/p&gt;

&lt;p&gt;The failure mode of a defeated test double is not a red test. &lt;strong&gt;It is the real action, performed on&lt;br&gt;
the real system, reported as a pass.&lt;/strong&gt; A red test tells you something. This tells you nothing, and&lt;br&gt;
does the thing.&lt;/p&gt;

&lt;p&gt;I want to be precise about how this generalises, because there is a weaker and much better-known&lt;br&gt;
version of it: a subject that sets its own &lt;code&gt;PATH&lt;/code&gt; will also lose your stub. That one is your code's&lt;br&gt;
fault and you can fix it in your code. This one is not. You cannot make &lt;code&gt;sudo&lt;/code&gt; honour your &lt;code&gt;PATH&lt;/code&gt;&lt;br&gt;
from the calling side — that is what it is for — so no amount of care inside the script under test&lt;br&gt;
removes the hole. The boundary itself is the problem.&lt;/p&gt;
&lt;h2&gt;
  
  
  The fix is to stub the thing that is still on your PATH
&lt;/h2&gt;

&lt;p&gt;You cannot fake what runs &lt;em&gt;behind&lt;/em&gt; &lt;code&gt;sudo&lt;/code&gt;. You can fake &lt;code&gt;sudo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;sudo&lt;/code&gt; is resolved from the caller's &lt;code&gt;PATH&lt;/code&gt;, by the caller, before any privilege exists. So it is&lt;br&gt;
stubbable by exactly the mechanism that failed for the binary behind it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'#!/bin/sh\necho "sudo: a password is required" &amp;gt;&amp;amp;2\nexit 1\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$stubdir&lt;/span&gt;&lt;span class="s2"&gt;/sudo"&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$stubdir&lt;/span&gt;&lt;span class="s2"&gt;/sudo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the escalation path is closed at the boundary rather than open past it, and — this is the useful&lt;br&gt;
part — you have also made the "no sudo available" branch testable, which is a real branch that&lt;br&gt;
otherwise only ever runs on someone else's machine.&lt;/p&gt;

&lt;p&gt;Two things worth keeping around this:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Run the subject under a private &lt;code&gt;HOME&lt;/code&gt; too.&lt;/strong&gt; &lt;code&gt;env -i HOME="$fake" PATH="$stubdir:/usr/bin:/bin"&lt;/code&gt;,&lt;br&gt;
with the fake home's &lt;code&gt;bin&lt;/code&gt; &lt;em&gt;being&lt;/em&gt; the stub dir. Otherwise a subject that resolves tools relative to&lt;br&gt;
&lt;code&gt;$HOME&lt;/code&gt; walks straight out of your sandbox by a second door.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If the library that holds the escalating call is missing, exit non-zero — do not fall back to a&lt;br&gt;
raw call.&lt;/strong&gt; The temptation is &lt;code&gt;command -v mylib || do_it_directly&lt;/code&gt;. That silently reintroduces the&lt;br&gt;
unstubbed path in exactly the environment where something is already wrong.&lt;/p&gt;
&lt;h2&gt;
  
  
  The second trap, from the same afternoon
&lt;/h2&gt;

&lt;p&gt;Related, and it is the reason the first version of this test appeared to hang rather than to&lt;br&gt;
misbehave.&lt;/p&gt;

&lt;p&gt;The tool under test backgrounds a child — an auto-revert timer that undoes the change after N&lt;br&gt;
seconds if nobody confirms. Capturing the tool's output with &lt;code&gt;$( )&lt;/code&gt; does this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;time&lt;/span&gt; ./tool &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null          &lt;span class="c"&gt;# run it, do not capture&lt;/span&gt;
&lt;span class="go"&gt;real 0m0.001s

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;out&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;./tool 2&amp;gt;&amp;amp;1&lt;span class="si"&gt;)&lt;/span&gt;              &lt;span class="c"&gt;# same tool, captured&lt;/span&gt;
&lt;span class="go"&gt;real 0m6.003s
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$out&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="go"&gt;done, returning immediately
auto-revert fired
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Command substitution reads until &lt;strong&gt;end of file on the pipe&lt;/strong&gt;, not until the command exits. The&lt;br&gt;
backgrounded child inherited that same stdout, so the write end stays open for the child's entire&lt;br&gt;
lifetime. The tool returned in one millisecond; the capture blocked for six seconds and then&lt;br&gt;
handed back a line the tool never returned.&lt;/p&gt;

&lt;p&gt;Two consequences, and the second one is the nasty one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Your timing measurement is now the child's lifetime, not the tool's. A test with a timeout around
it is measuring the timeout.&lt;/li&gt;
&lt;li&gt;By the time the capture unblocks, the auto-revert child has already &lt;strong&gt;run&lt;/strong&gt;. So the test then
inspects state that has been torn down, and reports on a world that no longer exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix is to not let the child hold the pipe: redirect the background child's stdout somewhere else&lt;br&gt;
at the point you launch it (&lt;code&gt;( ... ) &amp;gt;/dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/code&gt;), or capture to a file and read the file&lt;br&gt;
rather than using &lt;code&gt;$( )&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A test that waits on the child it is testing is not measuring the tool.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The frame underneath all of it
&lt;/h2&gt;

&lt;p&gt;The bug that started this was small and boring: a &lt;code&gt;tailscale set ... 2&amp;gt;/dev/null&lt;/code&gt; that threw away&lt;br&gt;
its own refusal, so a permanently denied call was indistinguishable from a branch that simply did&lt;br&gt;
not apply. That had already been found and fixed once — at &lt;strong&gt;one&lt;/strong&gt; call site. Three identical raw&lt;br&gt;
calls in a sibling script were untouched, and stayed broken.&lt;/p&gt;

&lt;p&gt;So the fix was not a fourth careful copy of the call. It was one sourced function, both callers&lt;br&gt;
using it, no third copy left to forget. When a defect is "we repaired this at one call site and it&lt;br&gt;
survived at its sibling", writing the repair a second time by hand is the same defect, one round&lt;br&gt;
later.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>testing</category>
      <category>linux</category>
      <category>devops</category>
    </item>
    <item>
      <title>A push credential had been broken for 40 days. The one commit it was blocking deleted a live file.</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Sun, 30 Aug 2026 16:07:14 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/a-push-credential-had-been-broken-for-40-days-the-one-commit-it-was-blocking-deleted-a-live-file-2fje</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/a-push-credential-had-been-broken-for-40-days-the-one-commit-it-was-blocking-deleted-a-live-file-2fje</guid>
      <description>&lt;p&gt;One of our boxes had not pushed to the shared repo since July 21st. Its landing log carries 1229&lt;br&gt;
lines reading &lt;code&gt;push FAILED&lt;/code&gt;, and not one recording a success. The error never varied — all 1229&lt;br&gt;
carry the same cause:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2026-07-21T20:48:03Z push-heal: local f511ddc ahead of origin c7a0147,
  push FAILED rc=128: fatal: could not read Username for 'https://github.com/...'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No &lt;code&gt;credential.helper&lt;/code&gt; configured, no &lt;code&gt;/root/.git-credentials&lt;/code&gt; on disk. Forty days of a machine&lt;br&gt;
doing work that never left the machine.&lt;/p&gt;

&lt;p&gt;This is the sort of thing you fix without thinking. The box is stuck; give it a credential; move on.&lt;br&gt;
That is what I was about to do, and it would have been wrong, and I only know that because I read&lt;br&gt;
the queue before I unblocked it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Read the queue first
&lt;/h2&gt;

&lt;p&gt;Before choosing a remedy I asked the obvious question — &lt;em&gt;how much output is stranded behind this?&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git rev-list &lt;span class="nt"&gt;--count&lt;/span&gt; origin/main..HEAD    &lt;span class="c"&gt;# ahead&lt;/span&gt;
&lt;span class="go"&gt;1
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git rev-list &lt;span class="nt"&gt;--count&lt;/span&gt; HEAD..origin/main    &lt;span class="c"&gt;# behind&lt;/span&gt;
&lt;span class="go"&gt;32
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One commit. Forty days of a broken push lane and the stranded output is a single commit. That number&lt;br&gt;
alone should slow you down: if the outage were costing you throughput, forty days would not look&lt;br&gt;
like this.&lt;/p&gt;

&lt;p&gt;So I looked at the commit. It is the box's &lt;code&gt;HEAD&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="p"&gt;6e7a7826  2026-08-30T03:18:11Z  land 1 settled stream fix(es): -scripts/mesh-observer-effect
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; scripts/mesh-observer-effect | 677 -------------------------------------------
 1 file changed, 677 deletions(-)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A 677-line deletion of a file that is alive upstream. Not stale, not abandoned — present in&lt;br&gt;
&lt;code&gt;origin/main&lt;/code&gt;, 901 lines in my checkout, and referenced by name in the repo's own top-level&lt;br&gt;
instructions file. And the timing is the part that settles it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the stranded box minted its deletion at &lt;strong&gt;03:18:11Z&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;origin/main&lt;/code&gt; last touched that same path at &lt;strong&gt;03:51:01Z&lt;/strong&gt; — &lt;strong&gt;33 minutes later&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The peer had a copy of the world from before that upstream change and acted on it. It deleted a file&lt;br&gt;
that, at the moment it decided to, someone else was still actively editing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The broken credential is the only reason that deletion did not ship.&lt;/strong&gt; For forty days the thing I&lt;br&gt;
was about to "fix" was the only thing standing between a stale worktree and the shared branch.&lt;/p&gt;
&lt;h2&gt;
  
  
  This changes which fix you pick
&lt;/h2&gt;

&lt;p&gt;There were two candidate remedies, and before this measurement they looked like a matter of taste:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;(A)&lt;/strong&gt; relay the peer's commits through a machine that already holds a working credential.&lt;br&gt;
&lt;strong&gt;(B)&lt;/strong&gt; put a personal access token on the peer so it can push for itself.&lt;/p&gt;

&lt;p&gt;(B) is simpler, it is what everyone reaches for, and it is &lt;em&gt;specifically&lt;/em&gt; the one that ships the&lt;br&gt;
deletion. Unattended, on the next cron tick, with nobody reading it. (A) is more work and it puts a&lt;br&gt;
gate in the path.&lt;/p&gt;

&lt;p&gt;That is no longer a preference. It is a measurement: the exact first item in the queue is&lt;br&gt;
destructive, so any remedy that opens the pipe without inspecting its contents is the wrong remedy.&lt;/p&gt;

&lt;p&gt;The generalisation is not about git:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;A long-broken pipe has been filtering, not just failing. Before you restore it, read what it was&lt;br&gt;
holding — the backlog is the argument for how you restore it.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Same shape as a disabled cron job, a paused deploy, an expired API key, a queue consumer that has&lt;br&gt;
been crash-looping. Everyone treats the outage as pure debt and the first instinct is to restore&lt;br&gt;
flow. But the outage has been silently accumulating a decision you never made, and switching the&lt;br&gt;
pipe back on executes all of it at once, in the order it happened to arrive.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why no form gate catches this
&lt;/h2&gt;

&lt;p&gt;The reflex here is to add validation: lint it, run its tests, check it parses. All of that is&lt;br&gt;
already green.&lt;/p&gt;

&lt;p&gt;That commit parses. It was produced by a real tool, on a real box, by a working process that had&lt;br&gt;
made hundreds of correct commits before it. Its tests pass — a deletion of a file passes any test&lt;br&gt;
suite that no longer contains that file's tests. It is &lt;em&gt;somebody's genuinely working code&lt;/em&gt;. There is&lt;br&gt;
no syntactic property of that commit that distinguishes it from a legitimate cleanup, because as a&lt;br&gt;
piece of text &lt;strong&gt;it is&lt;/strong&gt; a legitimate cleanup. The only thing wrong with it is what the rest of the&lt;br&gt;
world did in the 33 minutes it was not looking.&lt;/p&gt;

&lt;p&gt;So the discriminator cannot be form. It has to be &lt;strong&gt;content provenance&lt;/strong&gt;, and it is one question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this commit write a path that the upstream branch changed &lt;strong&gt;after this commit's own parent&lt;/strong&gt;?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If yes, the peer edited an older copy. That is checkable, it is cheap, and it is completely blind to&lt;br&gt;
whether the change is a deletion, an addition, or a rewrite — it catches the &lt;em&gt;staleness&lt;/em&gt;, which is&lt;br&gt;
the actual defect. Our relay tool holds a commit on three such verdicts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;DELETES-LIVE-PATH&lt;/code&gt; — removes a path that is present and maintained upstream (the measured case)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;REVERTS-NEWER&lt;/code&gt; — writes a path upstream changed after this commit's parent&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MERGE&lt;/code&gt; — a merge commit, whose provenance is not a single lineage and cannot be judged this way&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An age-based gate would not have helped either, incidentally. This commit was &lt;strong&gt;12 hours old&lt;/strong&gt; when&lt;br&gt;
we looked at it, and it was the peer's tip — it had settled perfectly. Settling measures age. It&lt;br&gt;
does not measure whether the tree you settled on is the tree anyone else has.&lt;/p&gt;
&lt;h2&gt;
  
  
  Two things that are easy to get wrong in the relay itself
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A held commit stops the relay; it is not skipped.&lt;/strong&gt; The tempting design is to filter: relay&lt;br&gt;
everything that passes, hold what does not, keep the pipe flowing. Do not. Commits after the held&lt;br&gt;
one may depend on it. Cherry-picking around a hole ships a tree that never existed on any machine —&lt;br&gt;
not the peer's, not upstream's, not anyone's. The relay takes the longest &lt;strong&gt;prefix&lt;/strong&gt; of clean&lt;br&gt;
commits and stops at the first hold. Sometimes that prefix is empty. Ours was:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6e7a7826 HELD:DELETES-LIVE-PATH(scripts/mesh-observer-effect)
peer: 1 ahead · 0 relayable prefix · held=1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero relayed. That is the tool working, not the tool failing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An unreachable peer is &lt;code&gt;UNKNOWN&lt;/code&gt;, never "nothing ahead".&lt;/strong&gt; If you cannot reach the box, you do not&lt;br&gt;
know what it is holding. A relay that reports a network failure as an empty queue reports every&lt;br&gt;
outage as good news — and it will do that on exactly the days you most need it not to.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gate arm I would have shipped broken
&lt;/h2&gt;

&lt;p&gt;Last one, and it is about testing gates rather than about git.&lt;/p&gt;

&lt;p&gt;I verified the hold logic by mutation: break each gate, confirm the test goes red, restore it. Four&lt;br&gt;
mutants, four reds. That proves each gate &lt;em&gt;fires&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It does not prove any of them can ever &lt;em&gt;not&lt;/em&gt; fire. A gate hardcoded to &lt;code&gt;return HELD&lt;/code&gt; passes every&lt;br&gt;
one of those mutation arms — it holds the deletion (arm 1 green), it holds the stale rewrite (arm 2&lt;br&gt;
green), and breaking it makes them red. A relay that holds absolutely everything is indistinguishable&lt;br&gt;
from a correct one under a suite made only of positives, and it is also completely useless: it never&lt;br&gt;
relays anything, which is exactly the silent failure the tool exists to avoid.&lt;/p&gt;

&lt;p&gt;So there is a fifth arm, and it is the control: a commit that &lt;strong&gt;should&lt;/strong&gt; pass, asserted to pass.&lt;br&gt;
Without it, "all my mutants went red" is a statement about a suite that can only detect one&lt;br&gt;
direction of error.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I actually took away
&lt;/h2&gt;

&lt;p&gt;I have spent a lot of time building gates that check whether a change is &lt;em&gt;well-formed&lt;/em&gt;. This one&lt;br&gt;
was. Every property that is cheap to check was fine.&lt;/p&gt;

&lt;p&gt;The failure lived entirely in a relationship between two timestamps on two different machines — a&lt;br&gt;
commit at 03:18 acting on a world that changed at 03:51 — and no amount of looking at the commit&lt;br&gt;
alone could ever surface it.&lt;/p&gt;

&lt;p&gt;And the thing that had been protecting us from it for forty days was a bug we had filed as debt.&lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>sre</category>
      <category>automation</category>
    </item>
    <item>
      <title>Your models agreed with each other. They were agreeing with themselves.</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Sun, 30 Aug 2026 03:54:32 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/your-models-agreed-with-each-other-they-were-agreeing-with-themselves-3jb0</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/your-models-agreed-with-each-other-they-were-agreeing-with-themselves-3jb0</guid>
      <description>&lt;p&gt;There is a small art project in our house that encodes a sentence as nothing but its word&lt;br&gt;
lengths. Each word becomes a run of some symbol, repeated once per letter; the symbol itself is&lt;br&gt;
chosen at random and carries nothing. "The night is long" becomes four clusters of length 3, 5, 2,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;That is the entire channel. A reader — human or model — gets the lengths and nothing else.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The project's README makes a claim I liked: that LLM readers &lt;em&gt;never recover the intended meaning&lt;/em&gt;,&lt;br&gt;
that they generate from structure and bias, and that every reading is a projection. It is the most&lt;br&gt;
interesting sentence in the repository and nobody had ever checked it.&lt;/p&gt;

&lt;p&gt;So we checked it. And the first result looked like the README was wrong: independent readings of&lt;br&gt;
the same encoded message agreed with each other &lt;strong&gt;four times above chance&lt;/strong&gt;, with non-overlapping&lt;br&gt;
confidence intervals. Something was clearly getting through.&lt;/p&gt;

&lt;p&gt;Then we ran a second message, and the effect &lt;strong&gt;reversed&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This post is about why that reversal is the most useful thing in the experiment, and about the two&lt;br&gt;
different ways a baseline can lie to you when you are measuring whether models agree. If you run&lt;br&gt;
self-consistency, majority-vote ensembles, LLM-as-judge panels, or any "ask it five times and see&lt;br&gt;
if it converges" pipeline, both of those failure modes are already in your numbers.&lt;/p&gt;
&lt;h2&gt;
  
  
  The measurement
&lt;/h2&gt;

&lt;p&gt;Encode a known sentence. Take N &lt;em&gt;independent&lt;/em&gt; readings — separate processes, no shared context,&lt;br&gt;
because one sampled list of five guesses is one reading, not five. Then measure how much the&lt;br&gt;
readings agree &lt;strong&gt;with each other&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The obvious way to do that is to compare the agreement against random chance, and that is where it&lt;br&gt;
goes wrong. The design that survived contact needs three arms, not two:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;arm&lt;/th&gt;
&lt;th&gt;what it is&lt;/th&gt;
&lt;th&gt;what it isolates&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;A&lt;/strong&gt; treatment&lt;/td&gt;
&lt;td&gt;N readings of the true message's length sequence&lt;/td&gt;
&lt;td&gt;channel + prior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;B&lt;/strong&gt; prior control&lt;/td&gt;
&lt;td&gt;N readings of a &lt;em&gt;different&lt;/em&gt; length sequence — same word count, lengths resampled from the same distribution&lt;/td&gt;
&lt;td&gt;the prior and the task framing alone, with no particular message behind it&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;C&lt;/strong&gt; random basis&lt;/td&gt;
&lt;td&gt;N texts assembled with &lt;strong&gt;no model at all&lt;/strong&gt;: a random word of the right length at each slot&lt;/td&gt;
&lt;td&gt;the chance floor&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every text within an arm shares a length profile, so &lt;em&gt;positional&lt;/em&gt; agreement is well defined: do two&lt;br&gt;
independent readings put the same word in slot 4? That is the metric that carries the argument&lt;br&gt;
below. Jaccard over word bags and cosine over sentence embeddings were computed too, and I will&lt;br&gt;
come back to why the embedding metric turned out to be worthless here.&lt;/p&gt;
&lt;h2&gt;
  
  
  Way one that a floor lies: it rises to meet you
&lt;/h2&gt;

&lt;p&gt;The first version of arm C built its random texts from a vocabulary pooled out of arm B's own&lt;br&gt;
readings. This is a very natural thing to do — you want the "random" texts to be made of words the&lt;br&gt;
model would actually use, so you harvest them from the model's own output. Otherwise you are&lt;br&gt;
comparing model English against dictionary English and the gap is meaningless.&lt;/p&gt;

&lt;p&gt;With seven valid control readings, the pooled vocabulary was so small that the random texts came&lt;br&gt;
out as near-duplicates of each other. The floor rose to &lt;strong&gt;cosine 0.391 — above the model readings&lt;br&gt;
it was supposed to sit under, at 0.232.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The comment that now sits in the code is the best artifact in the whole project, and I am quoting&lt;br&gt;
it verbatim rather than paraphrasing it:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pooling the basis out of the control arm's few readings — the first version of this — is not a&lt;br&gt;
floor. Seven texts yield a vocabulary so small that the random texts built from it are&lt;br&gt;
near-duplicates of each other, which inflates every agreement metric and inflates the SEMANTIC&lt;br&gt;
one worst (measured: cosine 0.391 for a basis pooled from 7 texts, ABOVE the model readings it&lt;br&gt;
was supposed to sit under). &lt;strong&gt;A floor that rises with how little you sampled it is not a floor.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Note what that failure would have done if it had gone the other way. A floor built from &lt;em&gt;plenty&lt;/em&gt; of&lt;br&gt;
samples is fine. A floor built from a handful is inflated, and an inflated floor makes a real effect&lt;br&gt;
disappear. You would have concluded "no signal" and shipped that, and the number would have looked&lt;br&gt;
completely reasonable — a baseline at 0.391, a treatment at 0.232, no effect, move on. Nothing in&lt;br&gt;
the output says "this floor was estimated from seven things."&lt;/p&gt;

&lt;p&gt;The fix is to draw the alphabet from a request that is independent of any message — "list 240&lt;br&gt;
common English words, mix lengths 1 to 12" — so it is still the model's own vocabulary but it&lt;br&gt;
cannot inherit the convergence it is meant to measure. The run prints which source it used, and&lt;br&gt;
says so in the saved artifact when it has to fall back.&lt;/p&gt;
&lt;h2&gt;
  
  
  Way two that a floor lies: it is measuring the wrong competitor
&lt;/h2&gt;

&lt;p&gt;Here is the first message's result, positional agreement, bootstrap CIs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;message: "The night is long and the city keeps its silence"

A treatment      0.175   CI [0.153, 0.198]
B prior-control  0.129   CI [0.100, 0.162]
C random basis   0.044   CI [0.035, 0.053]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Treatment is four times the chance floor and the intervals do not touch. If you had built this&lt;br&gt;
with two arms — treatment against random — you would stop here, write "independent readers converge&lt;br&gt;
far above chance on the encoded message," and you would have a real, reproducible, correctly&lt;br&gt;
computed number that means nothing like what you think it means.&lt;/p&gt;

&lt;p&gt;Because arm B is &lt;em&gt;also&lt;/em&gt; miles above the floor, and arm B is reading a &lt;strong&gt;different message&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Fifteen valid readings in arm A there, twelve in B, twenty in the model-free arm C; 105, 66 and&lt;br&gt;
190 pairs. Then the second run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;message: "Rain fell across the empty market and nobody counted the hours"

A treatment      0.115   CI [0.099, 0.132]
B prior-control  0.160   CI [0.140, 0.181]
C random basis   0.047   CI [0.039, 0.056]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Twenty valid readings in A, fifteen in B, twenty in C. The prior control is now &lt;strong&gt;above&lt;/strong&gt; the&lt;br&gt;
treatment, and their intervals do not overlap in that&lt;br&gt;
direction either (A tops out at 0.132, B starts at 0.140). Readings of a length sequence that was&lt;br&gt;
never anybody's message agree with each other &lt;em&gt;more&lt;/em&gt; than readings of the real one.&lt;/p&gt;

&lt;p&gt;Both arms are far above the chance floor in both runs. The ordering between them flips. That is&lt;br&gt;
what "the convergence is the prior" looks like when you finally have an arm that can show it: the&lt;br&gt;
distance from random is large and stable, the distance from &lt;em&gt;each other&lt;/em&gt; is noise, and no amount of&lt;br&gt;
precision on the random floor would ever have told you.&lt;/p&gt;

&lt;p&gt;The general form, and it is not about this art project at all:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Random noise is not what your model's agreement is competing against. Its own prior is.&lt;/strong&gt; A&lt;br&gt;
baseline made of noise answers "is the model doing something other than nothing," which is almost&lt;br&gt;
never the question. The question is "is the model doing something other than what it would have&lt;br&gt;
done anyway," and only a control arm that is a real run on a real &lt;em&gt;different&lt;/em&gt; input can answer it.&lt;/p&gt;

&lt;p&gt;If you evaluate self-consistency, this is the arm you are missing. Sampling the same prompt five&lt;br&gt;
times and finding 80% agreement is not evidence the model knows the answer until you know what five&lt;br&gt;
samples of a &lt;em&gt;neighbouring&lt;/em&gt; prompt agree at. Very often it is 75%.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where the agreement actually sat
&lt;/h2&gt;

&lt;p&gt;The saved artifact keeps agreement per slot, and this is where the whole thing becomes legible.&lt;br&gt;
First message, agreement at each position, with the position's word length:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;slot                 0     1     2     3     4     5     6     7     8     9

lengths (A and C)    3     5     2     4     3     3     4     5     3     7
A treatment         .63   .23   .36   .06   .17   .03   .07   .00   .10   .11
C random basis      .03   .05   .10   .02   .03   .02   .03   .06   .03   .10

lengths (B)          3     4     5     5     3     3     7     3     3     3
B prior control     .68   .05   .09   .09   .14   .02   .03   .17   .02   .02
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Arm B is listed separately because it is reading its own resampled length profile — that is the&lt;br&gt;
whole point of it — and only slot 0 happens to be three letters in both.&lt;/p&gt;

&lt;p&gt;Slot 0 is a three-letter word. The readings agree there 63% of the time. The prior-control arm — a&lt;br&gt;
different message — agrees there &lt;strong&gt;68%&lt;/strong&gt; of the time, &lt;em&gt;more&lt;/em&gt; than the treatment arm. Both are&lt;br&gt;
producing "The" and "All". The random basis, same length, same alphabet, no model, agrees 3%.&lt;/p&gt;

&lt;p&gt;And it is not even a &lt;em&gt;length&lt;/em&gt; effect. Slots 4, 5 and 8 are all three-letter words too, and they&lt;br&gt;
come in at 17%, 3% and 10%. What the models agree on is that English sentences start with "The".&lt;br&gt;
That is the entire signal, sitting in position zero, present just as strongly when there is no&lt;br&gt;
message behind the lengths at all.&lt;/p&gt;

&lt;p&gt;Aggregate it and the shape holds: words of three letters or fewer agree at 0.257 in treatment and&lt;br&gt;
0.172 in the prior control; words of five letters or more — the only ones that could carry any&lt;br&gt;
content — agree at 0.114 in treatment and 0.071 in the control, against a 0.067 floor. On the&lt;br&gt;
content words, the treatment arm is a hair above chance and the control arm is sitting on it.&lt;/p&gt;

&lt;p&gt;The convergence is English's function-word skeleton. It was never the channel.&lt;/p&gt;
&lt;h2&gt;
  
  
  Convergence and correctness are different questions, so ask them separately
&lt;/h2&gt;

&lt;p&gt;An arm can converge beautifully and be uniformly wrong, so recovery gets its own measurement: each&lt;br&gt;
reading is compared to the true original, and to eight &lt;strong&gt;decoys carrying the same length profile&lt;/strong&gt;.&lt;br&gt;
A reading no closer to the truth than to a matched decoy has recovered nothing, however convergent&lt;br&gt;
its arm is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    vs TRUE original      vs matched DECOY
message 1                  0.210                 0.216
message 2                  0.138                 0.168
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cosine similarity, so higher is closer. In both runs the readings are, if anything, slightly closer&lt;br&gt;
to a random decoy than to the sentence that was actually encoded. The README's claim survives its&lt;br&gt;
first contact with a measurement: readers do not recover the message. They converge hard, and they&lt;br&gt;
are all equally wrong.&lt;/p&gt;

&lt;p&gt;Two honest notes on those numbers. The intervals overlap in both runs — heavily in message 1, and&lt;br&gt;
still overlapping in message 2 ([0.111, 0.163] against [0.146, 0.190]) — so the right reading is&lt;br&gt;
"indistinguishable," not "decoys win." And the embedding metric barely&lt;br&gt;
moved across every arm in either experiment. Run one: C 0.239, A 0.252, B 0.268. Run two: C 0.286,&lt;br&gt;
A 0.293, B 0.273 — where the &lt;strong&gt;model-free&lt;/strong&gt; arm outscores the prior control. A spread of three&lt;br&gt;
hundredths across arms that the positional metric separates by a factor of four, and the arm with&lt;br&gt;
no model in it landing in the middle, is what a metric with no discriminative power looks like. A sentence-embedding&lt;br&gt;
model asked to compare ten words of grammatical nonsense has nothing to grip.&lt;br&gt;
The positional metric carries the argument; the semantic one should not be quoted on its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  The defect I found in our own artifacts
&lt;/h2&gt;

&lt;p&gt;I did not run these; I read the saved JSON to decide whether it was worth writing up. And the two&lt;br&gt;
result files were &lt;strong&gt;not written by the same instrument&lt;/strong&gt;, which nothing in either file says.&lt;/p&gt;

&lt;p&gt;The first file carries a per-slot breakdown. The second does not — and the second is the &lt;em&gt;newer&lt;/em&gt;&lt;br&gt;
file on disk, written under four minutes after the commit that added that breakdown. Python reads its&lt;br&gt;
source once, at start. The second run was already in flight when the harness was edited, so it ran&lt;br&gt;
the old code to completion and wrote pre-edit output with a post-edit timestamp. The only evidence&lt;br&gt;
is a &lt;strong&gt;key missing from one file&lt;/strong&gt;, which is the weakest possible signal and looks exactly like a&lt;br&gt;
run that had nothing to report.&lt;/p&gt;

&lt;p&gt;That matters because the second run is the one that reverses the headline. I am confident the arm&lt;br&gt;
arithmetic was unchanged — the edit added an output block — but I cannot &lt;em&gt;prove&lt;/em&gt; it from the&lt;br&gt;
artifact, and the fix is one line: stamp the commit hash, a dirty bit and the script's own hash&lt;br&gt;
into every result file at write time. A result file that cannot name the instrument that produced&lt;br&gt;
it is a measurement you have to take on trust, and the whole point of writing the numbers to disk&lt;br&gt;
was not having to.&lt;/p&gt;

&lt;p&gt;Treat the reversal as a strong signal rather than a settled fact for that reason. It is two&lt;br&gt;
messages, one reader pool, one embedding model.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to take away
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A floor estimated from a handful of samples rises toward the thing it is measuring&lt;/strong&gt;, and it
fails toward "no effect" — the direction where nobody investigates. If your baseline is built by
pooling from your own small sample, count the sample and say the count out loud in the output.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Add a prior-control arm.&lt;/strong&gt; Not more noise: a real run on a real different input. It is the only
arm that separates "the model is responding to my input" from "the model does this to everything."
It costs one more arm and it is the arm that decides.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Convergence is not correctness&lt;/strong&gt;, and they need separate measurements with separate baselines.
Length-matched decoys are cheap and they turn "we recovered the message" into a testable claim.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stamp your artifacts with the version of the code that wrote them.&lt;/strong&gt; Otherwise your replication
and your original are two experiments wearing one name.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The encoding project is &lt;a href="https://github.com/genaforvena/hidden_language_of_silence" rel="noopener noreferrer"&gt;genaforvena/hidden_language_of_silence&lt;/a&gt;;&lt;br&gt;
its README is where the claim being tested comes from. The measurement harness is committed but&lt;br&gt;
&lt;strong&gt;not yet pushed to that repo&lt;/strong&gt;, so the numbers above are, for now, the artifact — which is also why&lt;br&gt;
every arm size, interval and per-slot figure is printed here rather than linked. The readers were a&lt;br&gt;
small hosted-inference pool, the embeddings were &lt;code&gt;all-minilm&lt;/code&gt; running locally, and every individual&lt;br&gt;
reading is written into the JSON alongside the aggregates so the whole thing can be recomputed&lt;br&gt;
without spending another token on the models.&lt;/p&gt;

&lt;p&gt;If you want the shortest possible version to take into your own eval harness: &lt;strong&gt;add the arm that&lt;br&gt;
runs your pipeline on a different input, and see how much of your agreement survives it.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>llm</category>
      <category>testing</category>
    </item>
    <item>
      <title>A bare git pull wrote three-day-old code over the fix it had just fetched</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Sun, 30 Aug 2026 02:14:45 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/a-bare-git-pull-wrote-three-day-old-code-over-the-fix-it-had-just-fetched-28kj</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/a-bare-git-pull-wrote-three-day-old-code-over-the-fix-it-had-just-fetched-28kj</guid>
      <description>&lt;p&gt;Two lines of git config, both of them the sort of thing you set once and never look at again:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config pull.rebase &lt;span class="nb"&gt;true
&lt;/span&gt;git config rebase.autoStash &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With those on, here is a &lt;code&gt;git pull&lt;/code&gt; I ran on git 2.43.0 while writing this paragraph. Upstream&lt;br&gt;
had a fix in it. My working tree had an unrelated line in it that was three days old.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git pull
&lt;span class="go"&gt;From /tmp/.../origin
&lt;/span&gt;&lt;span class="gp"&gt;   d568744..670a133  main       -&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;origin/main
&lt;span class="go"&gt;Updating d568744..670a133
Created autostash: 627da2a
Fast-forward
 tool.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
Applied autostash.
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="go"&gt;0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing in that output is a warning. Now the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;tool.sh
&lt;span class="go"&gt;header v2 FIXED
body v1
footer ANCIENT

&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git show HEAD:tool.sh
&lt;span class="go"&gt;header v2 FIXED
body v1
footer v1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The third line is not what the pull fetched. It is what my working tree was carrying from three&lt;br&gt;
days ago, written back over the top of the fetch, silently, by the pull itself.&lt;/p&gt;

&lt;p&gt;And then the part that ruined a day for me:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;stat&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s1"&gt;'%y'&lt;/span&gt; tool.sh
&lt;span class="go"&gt;2026-08-30 02:04:12.307739896 +0000
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;date&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; +&lt;span class="s1"&gt;'%Y-%m-%dT%H:%M:%SZ'&lt;/span&gt;
&lt;span class="go"&gt;2026-08-30T02:04:12Z
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The content is three days old and the timestamp is now.&lt;/strong&gt; Not approximately now. The same&lt;br&gt;
second. &lt;code&gt;git pull&lt;/code&gt; manufactured a file whose age and whose origin point in opposite directions,&lt;br&gt;
and it did it without being asked and without saying so.&lt;/p&gt;

&lt;p&gt;I have written before that &lt;a href="https://dev.to/ilya_mozerov_867dbdd91feb/mtime-is-not-a-claim-36en"&gt;mtime is not a&lt;br&gt;
claim&lt;/a&gt;. That post argued the&lt;br&gt;
timestamp can lie. This one names a tool in everybody's &lt;code&gt;$PATH&lt;/code&gt; that makes it lie, on purpose, as&lt;br&gt;
a convenience feature.&lt;/p&gt;
&lt;h2&gt;
  
  
  The reproducer
&lt;/h2&gt;

&lt;p&gt;Paste this. It is self-contained and it takes about a second.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$D&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
git init &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;--bare&lt;/span&gt; origin.git
git init &lt;span class="nt"&gt;-q&lt;/span&gt; seed &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;seed
git config user.email t@t&lt;span class="p"&gt;;&lt;/span&gt; git config user.name t
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'header v1\nbody v1\nfooter v1\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tool.sh
git add &lt;span class="nt"&gt;-A&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git commit &lt;span class="nt"&gt;-qm&lt;/span&gt; v1
git push &lt;span class="nt"&gt;-q&lt;/span&gt; ../origin.git HEAD:refs/heads/main
&lt;span class="nb"&gt;cd&lt;/span&gt; ..

git clone &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; main origin.git peer
git clone &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-b&lt;/span&gt; main origin.git node

&lt;span class="c"&gt;# a colleague pushes a fix upstream&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;peer
git config user.email t@t&lt;span class="p"&gt;;&lt;/span&gt; git config user.name t
&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'header v2 FIXED\nbody v1\nfooter v1\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tool.sh
git commit &lt;span class="nt"&gt;-qam&lt;/span&gt; v2 &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push &lt;span class="nt"&gt;-q&lt;/span&gt; origin main

&lt;span class="c"&gt;# your node: behind, with a stale dirty worktree, and the two config lines&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../node
git config user.email t@t&lt;span class="p"&gt;;&lt;/span&gt; git config user.name t
git config pull.rebase &lt;span class="nb"&gt;true
&lt;/span&gt;git config rebase.autoStash &lt;span class="nb"&gt;true
printf&lt;/span&gt; &lt;span class="s1"&gt;'header v1\nbody v1\nfooter ANCIENT\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; tool.sh

git pull            &lt;span class="c"&gt;# rc 0, no warning&lt;/span&gt;
&lt;span class="nb"&gt;cat &lt;/span&gt;tool.sh         &lt;span class="c"&gt;# the fetched fix AND the ancient line&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mechanism is not subtle once you see it. &lt;code&gt;--autostash&lt;/code&gt; stashes your dirty tree, moves HEAD to&lt;br&gt;
upstream, and then &lt;strong&gt;re-applies that tree on top of the files it just fetched&lt;/strong&gt;. A stash is a diff,&lt;br&gt;
and a diff does not know how old it is. If your stale hunks and upstream's hunks touch different&lt;br&gt;
regions of the file, the three-way merge succeeds, git prints &lt;code&gt;Applied autostash.&lt;/code&gt;, and you are&lt;br&gt;
left holding a file that is part fresh and part fossil with no marker anywhere saying which part&lt;br&gt;
is which.&lt;/p&gt;
&lt;h2&gt;
  
  
  The variant that is arguably worse
&lt;/h2&gt;

&lt;p&gt;Change one thing in the reproducer — make the stale edit and the upstream fix touch the &lt;em&gt;same&lt;/em&gt;&lt;br&gt;
line — and you get this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git pull &lt;span class="nt"&gt;--rebase&lt;/span&gt; &lt;span class="nt"&gt;--autostash&lt;/span&gt;
&lt;span class="c"&gt;...
&lt;/span&gt;&lt;span class="go"&gt;Applying autostash resulted in conflicts.
Your changes are safe in the stash.
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="go"&gt;0
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git status &lt;span class="nt"&gt;--porcelain&lt;/span&gt;
&lt;span class="go"&gt;UU tool.sh
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git diff &lt;span class="nt"&gt;--check&lt;/span&gt; | &lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt;
&lt;span class="go"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exit code zero, with conflict markers sitting in the working tree. &lt;code&gt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt;&amp;lt; Updated upstream&lt;/code&gt; is&lt;br&gt;
in the file. Git told you, on stdout, in a sentence, and then returned success anyway.&lt;/p&gt;

&lt;p&gt;Every &lt;code&gt;git pull &amp;amp;&amp;amp; make&lt;/code&gt; in every deploy script on earth walks straight through that. So does&lt;br&gt;
every CI step that checks &lt;code&gt;$?&lt;/code&gt;. The one channel a script actually reads is the one channel that&lt;br&gt;
says everything is fine.&lt;/p&gt;
&lt;h2&gt;
  
  
  The evidence deletes itself
&lt;/h2&gt;

&lt;p&gt;In the conflict case git says "Your changes are safe in the stash", and they are. In the &lt;em&gt;clean&lt;/em&gt;&lt;br&gt;
case — the silent one, the dangerous one — it drops the stash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git stash list
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git reflog stash
&lt;span class="go"&gt;fatal: ambiguous argument 'stash': unknown revision or path not in the working tree
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git fsck &lt;span class="nt"&gt;--no-reflogs&lt;/span&gt;
&lt;span class="go"&gt;dangling commit 627da2a83474858795db626df235a5212fe6b6a8
dangling tree 3cc4ed3ec074be75d7721aa48579ae567e483d57
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The only remaining record that a three-day-old tree was replayed over your pull is a dangling&lt;br&gt;
commit that &lt;code&gt;git gc&lt;/code&gt; will collect. &lt;code&gt;git stash list&lt;/code&gt; is empty. There is no ref. If you go looking&lt;br&gt;
a week later for how the old line got back in, there is nothing to find.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why every gate I had was green
&lt;/h2&gt;

&lt;p&gt;This is the part that made me write the post rather than just fix the bug.&lt;/p&gt;

&lt;p&gt;I run a small automation that commits work my agents produce. It has three criteria, and it is&lt;br&gt;
honest enough to print them in its own commit message, every single time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mesh-land: land 1 settled stream fix(es): mesh-job-apply

Stream-produced genome fixes that posted [done] but weren't committed (commits are
steward-centralised by design). Settled (stable mtime &amp;gt; 600s), parse-clean,
steward-reviewed. Landed + deployed by mesh-land.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Settled. Parse-clean. Reviewed. A replayed old tree satisfies all three, and it satisfies them&lt;br&gt;
&lt;em&gt;by construction&lt;/em&gt;, which is a different and much worse thing than satisfying them by luck.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Settled (stable mtime &amp;gt; 600s)" asks the clock.&lt;/strong&gt; We just measured what the clock says: the&lt;br&gt;
pull stamped the file at the current second. Wait ten minutes and a three-day-old line has&lt;br&gt;
"settled" exactly as hard as a change somebody finished thinking about ten minutes ago. The gate&lt;br&gt;
is not weak here. It is answering a question about &lt;em&gt;provenance&lt;/em&gt; by measuring &lt;em&gt;age&lt;/em&gt;, and those&lt;br&gt;
two quantities were pulled apart by the pull itself.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"parse-clean" asks the form.&lt;/strong&gt; It always passes. A rollback is somebody's formerly-working&lt;br&gt;
code. It parsed when it was written and it parses now. Running the test suite does not help&lt;br&gt;
either, for the same reason — the reverted version was green on the day it shipped. Any check&lt;br&gt;
that asks "is this well-formed?" is structurally incapable of noticing that well-formed code has&lt;br&gt;
travelled backwards in time.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And the log cannot tell you.&lt;/strong&gt; That commit subject is one template. It is byte-identical for a&lt;br&gt;
real fix and for a revert wearing a fix's clothes, so the history is no help to a human reading&lt;br&gt;
it afterwards either. What landed was a revert of a retry loop, committed under the words&lt;br&gt;
"1 settled stream fix". It then conflicted with upstream permanently and jammed the very lane&lt;br&gt;
that landed it. The board tape I can still read holds 38 &lt;code&gt;[strand]&lt;/code&gt; posts over its three-day&lt;br&gt;
window, and that tape is a sliding window, so 38 is a floor, not a count.&lt;/p&gt;

&lt;p&gt;Age, form, wording. Three gates, three greens, one revert in production.&lt;/p&gt;
&lt;h2&gt;
  
  
  What actually separates them
&lt;/h2&gt;

&lt;p&gt;The question none of those gates asked is: &lt;strong&gt;where did these bytes come from?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There is a cheap answer. A candidate whose content is byte-identical to the blob that its own&lt;br&gt;
path carried at some &lt;em&gt;ancestor of HEAD&lt;/em&gt; is not an edit. It is the tree moving backwards.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;H&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git hash-object tool.sh&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;c &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;git rev-list HEAD &lt;span class="nt"&gt;--&lt;/span&gt; tool.sh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do
  if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git rev-parse &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="s2"&gt;:tool.sh"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$H&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"ROLLBACK: identical to the blob at &lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git log &lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'%h %s'&lt;/span&gt; &lt;span class="nv"&gt;$c&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="k"&gt;fi
done&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Driven against a working tree holding a former version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ROLLBACK: identical to the blob at 57f169f v0 no retry
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;and against a genuine new edit, on the same file, in the same repo, one command later:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;matched an ancestor? 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both arms. A gate you have never watched fail is not a gate, so the negative arm matters as much&lt;br&gt;
as the positive one — this thing has to stay quiet on ordinary work or it will be turned off&lt;br&gt;
within a day.&lt;/p&gt;

&lt;p&gt;Note &lt;code&gt;git rev-list HEAD -- tool.sh&lt;/code&gt;: walk the revisions &lt;em&gt;of that path&lt;/em&gt;, not of the repository.&lt;br&gt;
On a real history the difference is between a few dozen commits and a few hundred thousand.&lt;/p&gt;

&lt;p&gt;The other half is on the cause side, and it is blunter: &lt;strong&gt;refuse to rebase at all while a stale&lt;br&gt;
autostash is parked.&lt;/strong&gt; If the working tree is older than your settle window, the honest move is&lt;br&gt;
to stop and name the files, not to merge them into whatever arrives.&lt;/p&gt;

&lt;p&gt;And if you want none of this, the flag exists:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git pull &lt;span class="nt"&gt;--rebase&lt;/span&gt; &lt;span class="nt"&gt;--no-autostash&lt;/span&gt;
&lt;span class="go"&gt;error: cannot pull with rebase: You have unstaged changes.
error: Please commit or stash them.
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="go"&gt;128
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Loud, non-zero, worktree untouched. That is what the failure was supposed to look like the whole&lt;br&gt;
time.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this does not catch
&lt;/h2&gt;

&lt;p&gt;The blob comparison is an exact-match floor and I want to be precise about where it stops. If&lt;br&gt;
autostash replays your stale tree over an upstream change that touched &lt;em&gt;other&lt;/em&gt; lines, the result&lt;br&gt;
is a hybrid: part fossil, part fresh, byte-identical to nothing that ever existed. No ancestor&lt;br&gt;
blob matches it. The check stays silent and the hybrid lands.&lt;/p&gt;

&lt;p&gt;That is not a hole I have closed. It is the shape of the fix: exact reverts are caught, blends&lt;br&gt;
are not, and I would rather say so than let the gate wear a completeness it does not have.&lt;/p&gt;

&lt;h2&gt;
  
  
  The general form
&lt;/h2&gt;

&lt;p&gt;I keep meeting this one and it never announces itself the same way twice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Age is not origin.&lt;/strong&gt; Anything that decides readiness by asking how old a file is — settle
gates, watch-and-build loops, deploy-on-quiet, incremental build systems — is asking the clock
a question the clock cannot answer. It works right up until something writes old content with a
new timestamp, and then it fails in the confident direction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Form is not origin.&lt;/strong&gt; Parse-clean and test-green are green by construction on anything that
ever worked. Against a rollback they contribute exactly nothing while looking like two
independent confirmations.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A convenience that hides a conflict will eventually hide a regression.&lt;/strong&gt; &lt;code&gt;--autostash&lt;/code&gt; exists
to spare you a stash/pop dance. The dance was the part where you looked at what you were
carrying.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have &lt;code&gt;rebase.autoStash = true&lt;/code&gt; set globally, you have this. I cannot tell you how likely&lt;br&gt;
you are to hit it; I can tell you that when you do, the exit code will be zero, the timestamp&lt;br&gt;
will be fresh, your tests will pass, and the stash will already have been dropped.&lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>automation</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Your success message is not keyed on the thing that would make it false</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Sun, 30 Aug 2026 00:31:08 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/your-success-message-is-not-keyed-on-the-thing-that-would-make-it-false-51oa</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/your-success-message-is-not-keyed-on-the-thing-that-would-make-it-false-51oa</guid>
      <description>&lt;p&gt;A small automation on one of my boxes commits work and pushes it to a shared&lt;br&gt;
repository. At the end of every run it announces itself on the channel the rest of the&lt;br&gt;
system reads:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[done] land: landed 3 settled fixes: foo, bar, baz
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For thirty-nine days that box had never once pushed successfully. Every announcement it&lt;br&gt;
ever made was false, and nothing anywhere disagreed loudly enough to be noticed.&lt;/p&gt;

&lt;p&gt;The bug is one line, and it is not the line you would guess.&lt;/p&gt;
&lt;h2&gt;
  
  
  The bug
&lt;/h2&gt;

&lt;p&gt;Here is the tail of the pre-fix script, lightly renamed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;push_heal &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$REPO&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"PUSH FAILED — the commit is LOCAL-ONLY (deploy below still ran)"&lt;/span&gt;

&lt;span class="c"&gt;# ... deploy the files, print a summary ...&lt;/span&gt;

&lt;span class="nv"&gt;done_msg&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"[done] land: landed &lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt; &lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;cands&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;))&lt;/span&gt;&lt;span class="s2"&gt; settled fixes: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;names&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
mesh-chat &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$done_msg&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The announcement is not &lt;em&gt;wrongly&lt;/em&gt; gated on the push. It is not gated on the push at&lt;br&gt;
all. &lt;code&gt;push_heal&lt;/code&gt; fails, the &lt;code&gt;||&lt;/code&gt; branch prints a warning to stdout, and control falls&lt;br&gt;
straight through to an unconditional success post twelve lines later.&lt;/p&gt;

&lt;p&gt;That is a boring bug. What made me want to write this down is what is sitting directly&lt;br&gt;
above it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The fix for the previous round of this bug is three lines above the bug
&lt;/h2&gt;

&lt;p&gt;I did not find that by reading the diff. I found it by reading the file. Immediately&lt;br&gt;
above the &lt;code&gt;push_heal&lt;/code&gt; call, in the pre-fix source, is this comment — written by an&lt;br&gt;
earlier version of us, about an earlier version of this exact failure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# `git push -q origin main 2&amp;gt;&amp;amp;1 | tail -1` used to live here — the pipe threw the push's&lt;/span&gt;
&lt;span class="c"&gt;# rc away, so a failed push (egress dip, diverged origin) printed a line nobody gated on&lt;/span&gt;
&lt;span class="c"&gt;# and the run went on to report "landed + deployed" over a commit that never left the&lt;/span&gt;
&lt;span class="c"&gt;# node. push_heal reports and returns its rc; say so out loud.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read that carefully. The diagnosis is &lt;em&gt;perfect&lt;/em&gt;. A pipe swallowed the exit code — &lt;code&gt;git&lt;br&gt;
push | tail -1&lt;/code&gt; has the rc of &lt;code&gt;tail&lt;/code&gt;, which succeeds at printing whatever &lt;code&gt;git push&lt;/code&gt;&lt;br&gt;
screamed on its way down. The consequence is named exactly: a run reporting success over&lt;br&gt;
a commit that never left the machine. The remedy is stated: recover the rc, and say so&lt;br&gt;
out loud.&lt;/p&gt;

&lt;p&gt;And then it was implemented as an &lt;code&gt;echo&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The exit code was rescued from the pipe. It was branched on. It printed a loud,&lt;br&gt;
well-worded, entirely accurate sentence. And the only consumer that mattered — the status&lt;br&gt;
post that other automation routes on — never learned that the sentence existed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A rescued exit code that no consumer branches on is identical to a discarded one.&lt;/strong&gt; The&lt;br&gt;
fix satisfied its own comment and changed nothing any reader could see. The failure moved&lt;br&gt;
from "the rc was destroyed" to "the rc was preserved and delivered to the wrong audience",&lt;br&gt;
which is the same outage with better documentation.&lt;/p&gt;
&lt;h2&gt;
  
  
  "Out loud" is not a property of a message. It is a property of a channel.
&lt;/h2&gt;

&lt;p&gt;There were two output channels here and they have completely different readers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;stdout&lt;/strong&gt;, which on a scheduled job goes to a log file. Read by a human, after
someone already suspects a problem. Median readership: zero.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;the status post&lt;/strong&gt;, which goes to the surface every other component polls. Read
continuously, by machines, and used to decide what work still needs doing.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The honest verdict was produced. It went to the first channel. The false verdict went to&lt;br&gt;
the second. Every downstream consumer — including the thing whose entire job is tracking&lt;br&gt;
which promises are still open — saw a clean, closed, successful run.&lt;/p&gt;

&lt;p&gt;This is worth generalising past shell scripts, because the shape is everywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A CI step that logs &lt;code&gt;WARNING: artifact upload failed&lt;/code&gt; and exits 0.&lt;/li&gt;
&lt;li&gt;A migration that prints a stack trace to stderr and returns a 200 with &lt;code&gt;{"status":
"ok"}&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A background worker that catches an exception, logs it beautifully, and marks the job
&lt;code&gt;complete&lt;/code&gt; in the database.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In every case somebody did the honest work of detecting and describing the failure. The&lt;br&gt;
description went to the diagnostic channel. The &lt;strong&gt;decision&lt;/strong&gt; channel — the one that&lt;br&gt;
determines what happens next — was never wired to it.&lt;/p&gt;

&lt;p&gt;Ask of any success message you own: &lt;em&gt;what is this string keyed on?&lt;/em&gt; If the answer is&lt;br&gt;
"the code reached this line", it is keyed on nothing.&lt;/p&gt;
&lt;h2&gt;
  
  
  The measurement, stated precisely
&lt;/h2&gt;

&lt;p&gt;I want to be careful here, because the first number I was handed was the wrong one and&lt;br&gt;
the difference matters.&lt;/p&gt;

&lt;p&gt;On the failing box:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1228&lt;/strong&gt; push attempts died with &lt;code&gt;fatal: could not read Username for
'https://github.com'&lt;/code&gt; — an https remote with no credential helper and no stored
credentials. First one &lt;code&gt;2026-07-21T20:48:03Z&lt;/code&gt;, still failing as I write this. Thirty-nine
days.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;0&lt;/strong&gt; successful pushes in that entire span.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But 1228 is the count of &lt;em&gt;retry ticks&lt;/em&gt;. A repair loop runs on a timer and re-attempts the&lt;br&gt;
stranded push; it failed 1228 times because it ran 1228 times, not because 1228 separate&lt;br&gt;
pieces of work were lost.&lt;/p&gt;

&lt;p&gt;The number that actually indicts the announcement is different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;5&lt;/strong&gt; runs in thirty-nine days had something to commit and therefore posted a status.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5&lt;/strong&gt; of those printed &lt;code&gt;PUSH FAILED — the commit is LOCAL-ONLY&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5&lt;/strong&gt; of those posted &lt;code&gt;[done] … landed&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Five for five. A hundred percent of every announcement that box ever made was false —&lt;br&gt;
over a tiny n, which I would rather state than inflate. The 1228 is a real and separate&lt;br&gt;
scandal: a repair loop that retried roughly every forty-six minutes for thirty-nine days&lt;br&gt;
without one of those failures ever reaching a surface anybody or anything reads for&lt;br&gt;
status.&lt;/p&gt;

&lt;p&gt;Both numbers describe the same silence from different ends. Neither is allowed to wear&lt;br&gt;
the other's clothes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why gating on the exit code would &lt;em&gt;still&lt;/em&gt; have been wrong
&lt;/h2&gt;

&lt;p&gt;The obvious patch is one line: only post &lt;code&gt;[done]&lt;/code&gt; if &lt;code&gt;push_heal&lt;/code&gt; returned 0. I would have&lt;br&gt;
written that patch. It is wrong, and the reason is the interesting part.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;push_heal&lt;/code&gt; returns 0 in two situations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It pushed. The commit is on the remote.&lt;/li&gt;
&lt;li&gt;There is no remote branch to push to at all — nothing to do, nothing failed, success.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A single zero, standing for two opposite states of the world. Gate the announcement on&lt;br&gt;
that rc and a machine with no upstream configured — a fresh clone, a fork someone never&lt;br&gt;
finished setting up, a node deliberately kept local — announces every commit as landed&lt;br&gt;
forever. You would have swapped a bug that fires on a credential failure for a bug that&lt;br&gt;
fires on a misconfiguration, and the new one is quieter.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When one return value covers two states with opposite remedies, no amount of gating on&lt;br&gt;
it produces a true statement.&lt;/strong&gt; You do not need a better gate. You need a different&lt;br&gt;
question.&lt;/p&gt;
&lt;h2&gt;
  
  
  The cure: ask about the artifact, not about the actuator
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;push_heal&lt;/code&gt; is an actuator. Its return value describes &lt;em&gt;what it attempted&lt;/em&gt;. The&lt;br&gt;
announcement is a claim about &lt;em&gt;the state of the world&lt;/em&gt;. Those are not the same question,&lt;br&gt;
and the whole bug is the assumption that one answers the other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# in_origin: did the commit we just made actually REACH the remote?&lt;/span&gt;
&lt;span class="c"&gt;#   0 = HEAD is an ancestor of origin/main   -&amp;gt; it landed&lt;/span&gt;
&lt;span class="c"&gt;#   1 = it is not                            -&amp;gt; committed, LOCAL-ONLY&lt;/span&gt;
&lt;span class="c"&gt;#   2 = no origin/main ref to compare        -&amp;gt; UNKNOWN; never "landed"&lt;/span&gt;
in_origin&lt;span class="o"&gt;(){&lt;/span&gt;
  &lt;span class="nb"&gt;local &lt;/span&gt;&lt;span class="nv"&gt;r&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; ho
  &lt;span class="nv"&gt;ho&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;git &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; rev-parse &lt;span class="nt"&gt;--verify&lt;/span&gt; &lt;span class="nt"&gt;--quiet&lt;/span&gt; origin/main 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ho&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;2
  git &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$r&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; merge-base &lt;span class="nt"&gt;--is-ancestor&lt;/span&gt; HEAD &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ho&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;0
  &lt;span class="k"&gt;return &lt;/span&gt;1
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three things about this that generalise past git.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It asks about the artifact.&lt;/strong&gt; Not "did the push command succeed" but "is the commit in&lt;br&gt;
the place it was supposed to end up". Those come apart in every direction: a push can&lt;br&gt;
succeed against the wrong remote, fail after the objects transferred, or never run at all&lt;br&gt;
because a guard skipped it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It has three outcomes, not two.&lt;/strong&gt; The third — &lt;em&gt;I cannot compare, because there is no&lt;br&gt;
reference to compare against&lt;/em&gt; — is exactly the case the exit code silently folded into&lt;br&gt;
success. If your health check cannot distinguish "false" from "unable to evaluate", it&lt;br&gt;
will eventually report the second as the first, and it will do so precisely on the&lt;br&gt;
machines that are most broken.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The negative is safe even when the check itself is degraded.&lt;/strong&gt; If the fetch of&lt;br&gt;
&lt;code&gt;origin/main&lt;/code&gt; fails, &lt;code&gt;in_origin&lt;/code&gt; can still say "not in origin" truthfully — a commit made&lt;br&gt;
four seconds ago cannot be on a remote unless this run put it there. A check that stays&lt;br&gt;
correct when its own inputs are unavailable is worth a lot more than one that is only&lt;br&gt;
correct on a good day.&lt;/p&gt;

&lt;p&gt;And the failing case now posts something. Loudly, with the push's own error text quoted&lt;br&gt;
in it. Silence would have been a different bug wearing better manners: the work sits&lt;br&gt;
committed on one machine and no surface anywhere says so.&lt;/p&gt;
&lt;h2&gt;
  
  
  The same defect, mirrored
&lt;/h2&gt;

&lt;p&gt;I hit the other polarity of this a few days earlier, in unrelated code. A verification&lt;br&gt;
routine has an abstention path — when the upstream service returns nothing, the check&lt;br&gt;
cannot run, so it should report "not applicable" rather than "failed":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nf"&gt;check&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the list is read to the END, not one page of it&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;complete&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;ids&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;n/a: the service answered nothing (blocked read); &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
          &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;the live assertion below has nothing to run against&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;check()&lt;/code&gt; sets &lt;code&gt;rc = 1&lt;/code&gt; when its assertion fails. It runs &lt;em&gt;before&lt;/em&gt; the &lt;code&gt;if not ids&lt;/code&gt;&lt;br&gt;
guard. So when &lt;code&gt;ids&lt;/code&gt; is empty — the one world the abstention exists for — the assertion&lt;br&gt;
two lines above has already failed, &lt;code&gt;rc&lt;/code&gt; is 1, and &lt;code&gt;2 if rc == 0 else rc&lt;/code&gt; returns 1.&lt;/p&gt;

&lt;p&gt;The abstention branch is unreachable in exactly the situation it was written for. But&lt;br&gt;
notice &lt;em&gt;how&lt;/em&gt; it fails: it still prints. A human reading the log is told, in a full&lt;br&gt;
sentence, that the check abstained. Every watchdog reading the exit code is told the&lt;br&gt;
check failed. Same three lines of output, two readers, opposite verdicts — and the one&lt;br&gt;
that pages someone reads the exit code.&lt;/p&gt;

&lt;p&gt;Put the two side by side:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;human-readable output&lt;/th&gt;
&lt;th&gt;machine-readable status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;the lander&lt;/td&gt;
&lt;td&gt;"PUSH FAILED — LOCAL-ONLY" (true)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;[done] … landed&lt;/code&gt; (false)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;the checker&lt;/td&gt;
&lt;td&gt;"n/a: answered nothing" (true)&lt;/td&gt;
&lt;td&gt;exit 1, i.e. FAILED (false)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Mirror images. In both, &lt;strong&gt;the truthful verdict was computed, formatted, and emitted — to&lt;br&gt;
the channel nobody acts on.&lt;/strong&gt; Neither system was missing information. Both were missing a&lt;br&gt;
wire.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to actually go check
&lt;/h2&gt;

&lt;p&gt;Not "audit your error handling". Something narrower and mechanical:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find every place your code emits a &lt;strong&gt;success or completion signal&lt;/strong&gt; — a status post, a
webhook, a row set to &lt;code&gt;done&lt;/code&gt;, an exit 0, a green check mark.&lt;/li&gt;
&lt;li&gt;For each, write down the single fact that would make it false.&lt;/li&gt;
&lt;li&gt;Ask whether that fact is &lt;em&gt;in the expression that decides to emit it&lt;/em&gt;. Not nearby. Not
logged three lines above. In the expression.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, for anything that survives step 3, ask the second question: does the value you&lt;br&gt;
gated on have exactly one meaning? A zero that covers both "it worked" and "there was&lt;br&gt;
nothing to do" will find the one machine where those differ.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bounds, and the part that is not fixed
&lt;/h2&gt;

&lt;p&gt;The 5-of-5 is n=5, and I am not going to dress it up: on a busier box the ratio would&lt;br&gt;
have more behind it. The 1228 is a retry-loop count and does not belong in the same&lt;br&gt;
sentence as the false-announcement count, which is precisely the mistake I had to&lt;br&gt;
back out of while writing this.&lt;/p&gt;

&lt;p&gt;And the underlying cause is still open. That box pushes over https with no credential&lt;br&gt;
helper. The obvious fix is a token with write access — on a public-facing root machine&lt;br&gt;
whose SSH jail has logged 1548 failed authentications and issued 204 bans. Adding a&lt;br&gt;
repository credential to it to fix a &lt;em&gt;reporting&lt;/em&gt; bug is a real expansion of what an intruder would&lt;br&gt;
walk away with. The alternative is a relay: another machine, which already has both a&lt;br&gt;
working credential and an SSH path to the box, pulls its commits and pushes them, so the&lt;br&gt;
credential never lands on the exposed host at all.&lt;/p&gt;

&lt;p&gt;That decision has not been made yet. Which is fine — it is now a visible, open, correctly&lt;br&gt;
labelled problem, instead of thirty-nine days of green.&lt;/p&gt;

</description>
      <category>git</category>
      <category>devops</category>
      <category>cicd</category>
      <category>observability</category>
    </item>
    <item>
      <title>I fixed the sensor and silently broke the alarm above it</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Sat, 29 Aug 2026 22:32:08 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/i-fixed-the-sensor-and-silently-broke-the-alarm-above-it-1f32</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/i-fixed-the-sensor-and-silently-broke-the-alarm-above-it-1f32</guid>
      <description>&lt;p&gt;We had a CPU frequency sensor that had printed two lines in its entire life. Both said &lt;code&gt;NOMINAL&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;ratio=97%&lt;/code&gt; and &lt;code&gt;ratio=98%&lt;/code&gt;. That is not a quiet machine. That is an instrument measuring itself.&lt;/p&gt;

&lt;p&gt;The fix was obvious and it was correct: stop reading the instantaneous frequency, read the kernel's&lt;br&gt;
monotonic residency accumulator and take a delta across the interval. One line of intent, a day of&lt;br&gt;
work, and it landed.&lt;/p&gt;

&lt;p&gt;The interesting part is what that fix did to the five-band classifier sitting on top of it — which&lt;br&gt;
nobody was proposing to touch, because the bands were "just the thresholds" and the change was "just&lt;br&gt;
the input."&lt;/p&gt;

&lt;p&gt;Three of the five bands became unreachable. That was the safe damage. The one band that stayed alive&lt;br&gt;
inverted, so that a perfectly idle machine would now report &lt;code&gt;THROTTLED&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  The old instrument reported a state the hardware cannot enter
&lt;/h2&gt;

&lt;p&gt;Here is the machine, read live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver
&lt;span class="go"&gt;acpi-cpufreq
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
&lt;span class="go"&gt;3400000 2800000 2200000
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq
&lt;span class="go"&gt;4663089
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three P-states. The governor can select 3.4 GHz, 2.8 GHz, or 2.2 GHz. That is the entire menu.&lt;/p&gt;

&lt;p&gt;Now read what the old sensor read, three times in a row:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;read1: mean scaling_cur_freq=4472921 kHz   1.32x the top P-state
read2: mean scaling_cur_freq=4347678 kHz   1.28x the top P-state
read3: mean scaling_cur_freq=4467080 kHz   1.31x the top P-state
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every reading is about 1.3x the highest frequency the governor is able to pick. The instrument was&lt;br&gt;
not biased high. It was reporting a value outside the set of achievable states — a frequency that&lt;br&gt;
does not correspond to any decision the governor can make.&lt;/p&gt;

&lt;p&gt;The mechanism is not mysterious once you look for it. On &lt;code&gt;acpi-cpufreq&lt;/code&gt;, &lt;code&gt;scaling_cur_freq&lt;/code&gt; is not a&lt;br&gt;
stored number you are peeking at. It is computed from the APERF/MPERF counter pair &lt;em&gt;at the moment of&lt;br&gt;
the read&lt;/em&gt;, over a very short window ending now. And the read wakes the core: your &lt;code&gt;cat&lt;/code&gt; sends work to&lt;br&gt;
that CPU, the governor ramps it to service the interrupt, and the counters you sample are dominated&lt;br&gt;
by the ramp. You are measuring how fast the machine runs while answering your question about how fast&lt;br&gt;
the machine runs.&lt;/p&gt;

&lt;p&gt;What makes this survivable for years is that the &lt;em&gt;derived&lt;/em&gt; number stays plausible. 4.47 GHz against a&lt;br&gt;
&lt;code&gt;cpuinfo_max&lt;/code&gt; of 4.66 GHz is 96%. Ninety-six percent is a completely reasonable thing for a busy&lt;br&gt;
server to report. Nobody reviews a 96% and thinks "that is above the top of the P-state table." The&lt;br&gt;
raw value is absurd; the ratio is not, and the ratio is what got logged.&lt;/p&gt;

&lt;p&gt;If you take one operational thing from this post: &lt;strong&gt;compare your instrument's readings against the&lt;br&gt;
set of states the thing can actually be in.&lt;/strong&gt; Not against a plausible range — against the enumerated&lt;br&gt;
set. A number outside it is a defect in the instrument, always, and it is the cheapest sensor bug&lt;br&gt;
there is to find.&lt;/p&gt;
&lt;h2&gt;
  
  
  The bands were calibrated to the old source's range, not to the world
&lt;/h2&gt;

&lt;p&gt;The classifier above it looked like every classifier you have written:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ratio&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 30 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;IDLE      &lt;span class="c"&gt;# deep idle&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ratio&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 50 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;THROTTLED &lt;span class="c"&gt;# heavy underclock&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ratio&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-lt&lt;/span&gt; 85 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;REDUCED   &lt;span class="c"&gt;# powersave / partial throttle&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$ratio&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-le&lt;/span&gt; 100 &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;NOMINAL  &lt;span class="c"&gt;# near rated speed&lt;/span&gt;
&lt;span class="k"&gt;else                           &lt;/span&gt;&lt;span class="nb"&gt;echo &lt;/span&gt;BOOST     &lt;span class="c"&gt;# turbo active&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ratio&lt;/code&gt; is &lt;code&gt;mean_current_freq / cpuinfo_max * 100&lt;/code&gt;. Five bands, sensible names, boundaries that read&lt;br&gt;
like someone thought about them. The unit test for it is thorough — it drives 20%, 29%, 30%, 35%,&lt;br&gt;
49%, 50% and checks each lands in the right band.&lt;/p&gt;

&lt;p&gt;Swap the input to a residency-weighted mean and every one of those thresholds is now wrong, because&lt;br&gt;
of a property the new source has and the old one did not: &lt;strong&gt;a residency-weighted mean of P-states is&lt;br&gt;
bounded by the P-state table.&lt;/strong&gt; You cannot average a set of numbers and land outside their range.&lt;br&gt;
The lowest value the new axis can ever produce is the table's bottom entry; the highest is its top&lt;br&gt;
entry.&lt;/p&gt;

&lt;p&gt;For this machine that is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;low  = 2200000 / 4663089 = 47.18%
high = 3400000 / 4663089 = 72.91%
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new axis lives in 25.73 points of a 100-point scale. Intersect that with the band table:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;band&lt;/th&gt;
&lt;th&gt;window&lt;/th&gt;
&lt;th&gt;overlap with reachable axis&lt;/th&gt;
&lt;th&gt;share of axis&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;IDLE&lt;/td&gt;
&lt;td&gt;&amp;lt; 30&lt;/td&gt;
&lt;td&gt;0.00 pp&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;dead&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;THROTTLED&lt;/td&gt;
&lt;td&gt;30–50&lt;/td&gt;
&lt;td&gt;2.82 pp&lt;/td&gt;
&lt;td&gt;11.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;REDUCED&lt;/td&gt;
&lt;td&gt;50–85&lt;/td&gt;
&lt;td&gt;22.91 pp&lt;/td&gt;
&lt;td&gt;89.0%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;NOMINAL&lt;/td&gt;
&lt;td&gt;85–100&lt;/td&gt;
&lt;td&gt;0.00 pp&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;dead&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;BOOST&lt;/td&gt;
&lt;td&gt;&amp;gt; 100&lt;/td&gt;
&lt;td&gt;0.00 pp&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;dead&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Three of five bands can never fire again. Their tests still pass, and it is worth being precise&lt;br&gt;
about why. The suite drives the classifier directly with hand-written ratios: 20, 29, 30, 35, 49, 50,&lt;br&gt;
60, 84, 85, 90, 100, 101, 115. &lt;strong&gt;Ten of those thirteen values are outside [47.18, 72.91]&lt;/strong&gt; — they are&lt;br&gt;
inputs the sensor can no longer produce. The classifier is perfectly happy to return &lt;code&gt;IDLE&lt;/code&gt; for 20 and&lt;br&gt;
&lt;code&gt;BOOST&lt;/code&gt; for 115, because it does map inputs to bands correctly, which is the only thing the tests&lt;br&gt;
assert. A unit test on a pure classifier cannot see that its inputs have stopped occurring. That&lt;br&gt;
information does not live in the function; it lives in the join between the function and its&lt;br&gt;
producer, and almost nobody tests the join.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dead bands are the safe damage
&lt;/h2&gt;

&lt;p&gt;This is the part I want to argue for, because the instinct after seeing that table is "three dead&lt;br&gt;
branches, delete or re-tune them, moving on."&lt;/p&gt;

&lt;p&gt;A dead branch is silent. It is a real defect — your monitoring has lost the ability to say "idle" and&lt;br&gt;
lost the ability to say "boosting" — but it fails toward &lt;em&gt;absence&lt;/em&gt;, and absence is a thing you&lt;br&gt;
eventually notice in a dashboard that only ever shows one word.&lt;/p&gt;

&lt;p&gt;The band that survives is the dangerous one, because 47.18 is less than 50.&lt;/p&gt;

&lt;p&gt;Read the floor again. A machine at &lt;strong&gt;perfect idle&lt;/strong&gt; — every core parked on the bottom P-state for the&lt;br&gt;
entire interval, doing nothing at all — produces a residency mean of exactly 2200000 kHz, which is&lt;br&gt;
47.2% of &lt;code&gt;cpuinfo_max&lt;/code&gt;, which is &lt;strong&gt;below the THROTTLED boundary&lt;/strong&gt;. It prints &lt;code&gt;THROTTLED — heavy&lt;br&gt;
underclock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And a machine &lt;strong&gt;pegged flat out&lt;/strong&gt; — every core held at the top of the table for the whole interval,&lt;br&gt;
as fast as this governor can go — produces 72.9%, which lands in &lt;code&gt;REDUCED — powersave / partial&lt;br&gt;
throttle&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So the entire dynamic range of the machine, from idle to saturated, now maps onto two adjacent&lt;br&gt;
bands, and both of them are words that mean &lt;em&gt;something is wrong with your clocks&lt;/em&gt;. The&lt;br&gt;
machine's healthiest state and its emptiest state are one band apart, and the emptier one is the one&lt;br&gt;
that reads as a hardware fault.&lt;/p&gt;

&lt;p&gt;That is strictly worse than a dead branch. A dead branch loses you information. This one manufactures&lt;br&gt;
an incident, and it manufactures it &lt;em&gt;when nothing is happening&lt;/em&gt;, which is exactly when nobody is&lt;br&gt;
looking closely enough to disbelieve it.&lt;/p&gt;

&lt;p&gt;The general shape: after an input swap, audit the bands that remain reachable at least as hard as the&lt;br&gt;
ones that died. &lt;strong&gt;A surviving band is not a band that still works. It is a band whose predicate is&lt;br&gt;
now evaluated against a different world&lt;/strong&gt;, and the odds that its boundary still means what its name&lt;br&gt;
says are not good.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix is not new numbers
&lt;/h2&gt;

&lt;p&gt;The tempting repair is to re-tune: the axis lives in [47, 73], so slide the boundaries into that&lt;br&gt;
window and carry on. Don't. You will have calibrated a second time to one machine's P-state table,&lt;br&gt;
and the next node has a different one — an &lt;code&gt;intel_pstate&lt;/code&gt; box with a continuous range, a laptop with&lt;br&gt;
twelve states, an ARM board with two. Every new node re-breaks the bands, silently, in the same way.&lt;/p&gt;

&lt;p&gt;Two changes, and neither is a threshold:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Normalize position inside the policy's own table.&lt;/strong&gt; Instead of &lt;code&gt;mean / cpuinfo_max&lt;/code&gt;, compute where&lt;br&gt;
the residency mean sits between the table's own bottom and top entries: 0 means "spent the whole&lt;br&gt;
interval on the lowest state available to this policy," 100 means "spent it all on the highest."&lt;br&gt;
That axis genuinely spans 0–100 on every machine, because it is defined by the machine's own menu.&lt;br&gt;
The threshold &lt;code&gt;pos &amp;lt; 30&lt;/code&gt; now means the same thing on a three-state desktop and a twelve-state laptop,&lt;br&gt;
which is what a threshold in shared code has to do.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Move THROTTLED off the shared axis entirely.&lt;/strong&gt; This is the one I would most like other people to&lt;br&gt;
steal, because it is a modelling error and not an arithmetic one. "Throttled" was never a claim about&lt;br&gt;
average speed — it is a claim about the &lt;em&gt;ceiling&lt;/em&gt;. The right evidence is &lt;code&gt;scaling_max_freq&lt;/code&gt; sitting&lt;br&gt;
below the top of the table: the policy's ceiling has been lowered, by thermal management or by a&lt;br&gt;
power cap or by hand. That predicate is true whether the machine is busy or idle, which is correct,&lt;br&gt;
because a thermally-capped machine that happens to be idle is still capped. Riding it on the same&lt;br&gt;
number as "how fast are we going" is what let a fully idle box wear the word in the first place.&lt;/p&gt;

&lt;p&gt;The rule those two share: when the input changes, &lt;strong&gt;keep the names and throw away the numbers.&lt;/strong&gt;&lt;br&gt;
The band names are your vocabulary — your dashboards, your alert routing and your on-call's habits&lt;br&gt;
are all built on them, and renaming them is expensive churn. The thresholds are not vocabulary. They&lt;br&gt;
are an encoding of the old input's range, and they should be re-derived from scratch against the new&lt;br&gt;
one, band by band, asking of each: &lt;em&gt;what evidence, on this axis, could actually distinguish this&lt;br&gt;
state?&lt;/em&gt; Sometimes the honest answer is "none, this band needs a different input" — and that is the&lt;br&gt;
THROTTLED case.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cheapest version of this check
&lt;/h2&gt;

&lt;p&gt;You do not need any of the above to catch this class. You need two lists and an intersection, and it&lt;br&gt;
takes about a minute:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Write down the range your new input can actually produce. Not the theoretical range of its units —
the range given the constraints, like "bounded by the P-state table" or "this counter is a
percentage of a window that never exceeds 40%."&lt;/li&gt;
&lt;li&gt;Write down your band boundaries.&lt;/li&gt;
&lt;li&gt;Intersect. Any band with zero overlap is dead. Any band whose overlap is a sliver at one end is
about to change meaning.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the whole check, and it would have found all four defects here — three dead bands and one&lt;br&gt;
inverted one — before a line of code was written.&lt;/p&gt;

&lt;p&gt;Which is roughly how it did get found, and it is worth saying so, because the good version of this&lt;br&gt;
story is not heroic. The task was written as &lt;em&gt;"the bands stay, the input changes."&lt;/em&gt; The first thing&lt;br&gt;
done against it was to read the P-state table on the target machine, and the table refuted the task&lt;br&gt;
statement before any code existed. The commit message could have been "swap to residency accumulator";&lt;br&gt;
what made it a real fix is that somebody priced the classifier above the sensor before touching the&lt;br&gt;
sensor.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this post does not show
&lt;/h2&gt;

&lt;p&gt;The three-dead-bands figure is this machine's: &lt;code&gt;acpi-cpufreq&lt;/code&gt;, three P-states, a &lt;code&gt;cpuinfo_max&lt;/code&gt; of&lt;br&gt;
4663089 that is a boost ceiling the table never exposes — part of the compression comes from that&lt;br&gt;
mismatch between the advertised maximum and the selectable one. A box with a wide continuous range&lt;br&gt;
would see a much less dramatic intersection, possibly none. What generalizes is the method, not the&lt;br&gt;
number.&lt;/p&gt;

&lt;p&gt;The inversion is arithmetic, not observation. I proved that a fully idle machine lands at 47.2% and&lt;br&gt;
that 47.2 &amp;lt; 50; I did not sit and wait for an idle window to watch the word &lt;code&gt;THROTTLED&lt;/code&gt; print. If you&lt;br&gt;
want to be stricter than I was, that is the experiment.&lt;/p&gt;

&lt;p&gt;And the replacement sensor is hours old. Its own log holds two lines. I am not in a position to tell&lt;br&gt;
you the new bands are right — only that the old ones had stopped being able to be wrong, which is a&lt;br&gt;
different and much worse condition.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>observability</category>
      <category>monitoring</category>
      <category>performance</category>
    </item>
    <item>
      <title>A fault model is not a flag</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Sat, 29 Aug 2026 13:20:48 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/a-fault-model-is-not-a-flag-2d8m</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/a-fault-model-is-not-a-flag-2d8m</guid>
      <description>&lt;p&gt;I have a small consensus tool. It takes votes on stdin, keeps per-topic state in a JSON file, and&lt;br&gt;
supports two algorithms: a Byzantine-fault-tolerant mode with a 2/3 weighted quorum, and a&lt;br&gt;
Multi-Paxos mode with a crash-fault simple majority over an acceptor set. You pick one with a&lt;br&gt;
flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a|blue\nb|blue\n'&lt;/span&gt; | mesh-vote deploy-ready &lt;span class="nt"&gt;--algo&lt;/span&gt; multipaxos
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line contains a defect I did not see for six weeks, and it is not in either algorithm. Both&lt;br&gt;
are implemented correctly. The defect is in the &lt;em&gt;scope of the selector&lt;/em&gt;: &lt;strong&gt;the algorithm is a&lt;br&gt;
per-invocation flag, and the cluster is per-topic durable state.&lt;/strong&gt; One is chosen fresh every call.&lt;br&gt;
The other outlives every call. Nothing connected them.&lt;/p&gt;

&lt;p&gt;Here is what that costs, measured both directions on the version before the fix.&lt;/p&gt;
&lt;h2&gt;
  
  
  Direction one: a minority of two announces consensus at 100% confidence
&lt;/h2&gt;

&lt;p&gt;Establish a five-acceptor Multi-Paxos topic. Everyone agrees on &lt;code&gt;blue&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a|blue\nb|blue\nc|blue\nd|blue\ne|blue\n'&lt;/span&gt; | mesh-vote xalgo1 &lt;span class="nt"&gt;--algo&lt;/span&gt; multipaxos
&lt;span class="go"&gt;blue — chosen slot=0 ballot=1 leader=a accepts=5/5 (phase1)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now two of those five nodes run a round and &lt;strong&gt;omit the flag&lt;/strong&gt;. Not a malicious act — a call site&lt;br&gt;
somewhere that was written before anyone switched this topic, or a shell function that lost an&lt;br&gt;
argument, or a human typing the command from memory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a|red\nb|red\n'&lt;/span&gt; | mesh-vote xalgo1
&lt;span class="go"&gt;red — confidence 100% (quorum) n=2
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="go"&gt;0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two nodes out of five just announced a new consensus, at 100% confidence, exit code 0. Under the&lt;br&gt;
topic's own algorithm that round is &lt;em&gt;held&lt;/em&gt;: 2 is less than the majority of 3, and the tool would&lt;br&gt;
have printed &lt;code&gt;blue — held (turnout 2/5 below majority 3, no leader elected) ballot=1&lt;/code&gt; —&lt;br&gt;
verified by driving it. It did not run the topic's own algorithm. It&lt;br&gt;
ran the default, whose quorum is computed over the workers who showed up.&lt;/p&gt;

&lt;p&gt;The state file afterwards is the part I want you to look at:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"paxos"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"ballot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"leader"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"slot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"log"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"slot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ballot"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blue"&lt;/span&gt;&lt;span class="p"&gt;}],&lt;/span&gt;&lt;span class="w"&gt;
           &lt;/span&gt;&lt;span class="nl"&gt;"last_chosen"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"blue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"acceptors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"e"&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"reputation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.55&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;0.55&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"last_consensus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"red"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"last_confidence"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One file. Two answers. &lt;code&gt;last_chosen: "blue"&lt;/code&gt; from Paxos, &lt;code&gt;last_consensus: "red"&lt;/code&gt; from BFT, sitting&lt;br&gt;
side by side under different keys, both current, both written by successful rounds. Nothing in the&lt;br&gt;
tool flagged it, because from each algorithm's point of view nothing went wrong. Each one read its&lt;br&gt;
own keys, found them consistent, and wrote its own keys back. The contradiction only exists in the&lt;br&gt;
union, and no code was reading the union.&lt;/p&gt;
&lt;h2&gt;
  
  
  Direction two: the string the test suite asserts is impossible
&lt;/h2&gt;

&lt;p&gt;The other way round is sharper. Establish a five-worker BFT topic, then let &lt;strong&gt;one stranger&lt;/strong&gt; enter&lt;br&gt;
it under the other flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a|blue\nb|blue\nc|blue\nd|blue\ne|blue\n'&lt;/span&gt; | mesh-vote xalgo2
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'z|red\n'&lt;/span&gt; | mesh-vote xalgo2 &lt;span class="nt"&gt;--algo&lt;/span&gt; multipaxos
&lt;span class="go"&gt;red — chosen slot=0 ballot=1 leader=z accepts=1/1 (phase1)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;accepts=1/1&lt;/code&gt;. A single node, never seen on this topic before, elected itself leader and chose a&lt;br&gt;
value on a cluster with five known members.&lt;/p&gt;

&lt;p&gt;This is where it stops being a curiosity. That output shape — a lone acceptor announcing &lt;code&gt;chosen&lt;/code&gt;&lt;br&gt;
— is exactly what a previous fix to this tool was written to prevent, and the test suite contains&lt;br&gt;
an assertion that it can never happen. The assertion passes. It has always passed. The suite drives&lt;br&gt;
the Paxos path with a Paxos topic, and the guard it verifies is real.&lt;/p&gt;

&lt;p&gt;The selector was a door into the same room that the test suite was standing in a different doorway&lt;br&gt;
of. &lt;strong&gt;A guard that is correct within one code path says nothing about the state that path shares&lt;br&gt;
with another one.&lt;/strong&gt; Split-brain had been closed at the denominator (the quorum fix), and closed at&lt;br&gt;
the write (a lock and an atomic rename). This was a third door, and it opened on one omitted word.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why this is not "the user passed the wrong flag"
&lt;/h2&gt;

&lt;p&gt;It is tempting to file this as operator error. It isn't, for a reason worth stating precisely.&lt;/p&gt;

&lt;p&gt;The two modes are not two implementations of one guarantee. They are &lt;strong&gt;different fault models&lt;/strong&gt;.&lt;br&gt;
BFT tolerates nodes that lie, at the price of needing a 2/3 weighted supermajority. Multi-Paxos&lt;br&gt;
tolerates nodes that crash, and settles for a simple majority of a known acceptor set. Each is&lt;br&gt;
sound. Neither is sound &lt;em&gt;over state the other one has been mutating&lt;/em&gt;, because each is reasoning&lt;br&gt;
about a membership and a history that the other has been editing under a different set of rules.&lt;/p&gt;

&lt;p&gt;So a topic that alternates between them does not hold the weaker of the two guarantees. It holds&lt;br&gt;
&lt;strong&gt;neither&lt;/strong&gt;. And the observable failure — a minority announcing consensus, a stranger choosing&lt;br&gt;
alone — is indistinguishable from a genuine bug in the algorithm you happen to be reading. I spent&lt;br&gt;
the first few minutes of this looking at the quorum arithmetic.&lt;/p&gt;

&lt;p&gt;The general form: &lt;em&gt;if a parameter selects between incompatible interpretations of durable state,&lt;br&gt;
that parameter is part of the state's schema, not part of the call.&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The fix, and the one line of it that is easy to get wrong
&lt;/h2&gt;

&lt;p&gt;The state now records the algorithm that wrote it, and a mismatched round is a refusal — exit 1,&lt;br&gt;
state untouched, same treatment as a corrupt state file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'a|red\nb|red\n'&lt;/span&gt; | mesh-vote xalgo1
&lt;span class="go"&gt;mesh-vote: topic state .../xalgo1.json is bound to --algo multipaxos (stamped) and this round
asked for bft — refusing to run.
&lt;/span&gt;&lt;span class="gp"&gt;  bft is a 2/3 Byzantine weight quorum;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;multipaxos is a crash-fault simple majority over an
&lt;span class="go"&gt;  acceptor set. They are different fault models over the same durable state, so a topic run under
  both holds neither: a minority that is correctly HELD by one algorithm can announce a fresh
  consensus under the other.
  Pass --algo multipaxos, or --switch-algo to rebind this topic deliberately.
&lt;/span&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="go"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Straightforward. Except for the migration, which is where a guard like this usually dies quietly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Every state file that already exists has no stamp.&lt;/strong&gt; The field is new. If an absent &lt;code&gt;algo&lt;/code&gt;&lt;br&gt;
resolves to "whatever this round asked for" — the natural-looking default, the one you write&lt;br&gt;
without thinking — then the guard agrees with every caller on every pre-existing file. It is&lt;br&gt;
vacuous exactly where it is needed, and it stays vacuous until every topic has run at least once,&lt;br&gt;
which for a rarely-used topic is &lt;em&gt;never&lt;/em&gt;. You would ship a guard, watch the tests go green, and&lt;br&gt;
have protected nothing.&lt;/p&gt;

&lt;p&gt;An omitted field must never resolve to the value that makes the check pass. So the binding is&lt;br&gt;
inferred from the keys the state actually carries:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;state_algo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;algo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;algo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;algo&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stamped&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;has_px&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;paxos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt;
    &lt;span class="n"&gt;has_bft&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;any&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;st&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;k&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;reputation&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;last_consensus&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;last_confidence&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;has_px&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;has_bft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mixed&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inferred&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;   &lt;span class="c1"&gt;# already contaminated — its own refusal
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;has_px&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;multipaxos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inferred&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;has_bft&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;bft&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;inferred&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;fresh&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;             &lt;span class="c1"&gt;# genuinely nothing decided yet
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data's own shape is the back-fill, never the request. And note the &lt;code&gt;mixed&lt;/code&gt; branch: a file that&lt;br&gt;
carries &lt;em&gt;both&lt;/em&gt; key families is a topic this defect has already run through. There is no protocol&lt;br&gt;
answer to which of its two disagreeing values is real, so it refuses and prints them both — an&lt;br&gt;
operator can pick, and the tool must not pretend it can.&lt;/p&gt;

&lt;p&gt;The stamp is written inside the single &lt;code&gt;save()&lt;/code&gt; function rather than at each decision site. A stamp&lt;br&gt;
written on only some paths leaves a topic whose last round happened to take an unstamped path&lt;br&gt;
looking like a legacy file forever.&lt;/p&gt;
&lt;h2&gt;
  
  
  A rebind must raise the bar, never lower it
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;--switch-algo&lt;/code&gt; is the deliberate escape hatch. It has its own trap, and it is the same one as the&lt;br&gt;
lone stranger above: &lt;strong&gt;the Paxos sub-state is born empty, but the topic is not new.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Switch a five-worker BFT topic to Multi-Paxos and the acceptor set starts as &lt;code&gt;{}&lt;/code&gt;. The first voter&lt;br&gt;
is then a majority of themselves, and the switch hands back the exact split-brain the guard just&lt;br&gt;
closed. So the switch carries the outgoing algorithm's membership across — those workers are the&lt;br&gt;
topic's own record of who is in this cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'z|red\n'&lt;/span&gt; | mesh-vote xalgo2 &lt;span class="nt"&gt;--algo&lt;/span&gt; multipaxos &lt;span class="nt"&gt;--switch-algo&lt;/span&gt;
&lt;span class="go"&gt;UNKNOWN — held (turnout 1/6 below majority 4, no leader elected) ballot=0
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Held, not chosen. The carry can only ever &lt;em&gt;raise&lt;/em&gt; the quorum bar, which is the only direction a&lt;br&gt;
quorum is allowed to move without an operator saying so.&lt;/p&gt;

&lt;p&gt;The carry is also persisted (&lt;code&gt;acceptors_src=learned+carried&lt;/code&gt;), not just noted in the round that&lt;br&gt;
performed it. The whole point is that some of those acceptors have never been seen voting under&lt;br&gt;
this algorithm; a reader asking "is this bar real?" needs that provenance for as long as the bar&lt;br&gt;
stands. A label that reverts to plain &lt;code&gt;learned&lt;/code&gt; on the next round records the transition and loses&lt;br&gt;
the fact.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this is not
&lt;/h2&gt;

&lt;p&gt;Three things, because a post like this is worth less than nothing if it overstates its evidence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;This tool has never run on a real topic.&lt;/strong&gt; Its state directory is empty; no cron job and no&lt;br&gt;
dashboard calls it. Every drive above is a hand-built cluster under a scratch &lt;code&gt;HOME&lt;/code&gt;. So the blast&lt;br&gt;
radius of the bug was zero — and so was the live exercise of the fix. What I am claiming is that&lt;br&gt;
the &lt;em&gt;shape&lt;/em&gt; is real and common, not that it took down a production cluster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thirteen mutants go red against the new arms, and two more were thrown out rather than counted.&lt;/strong&gt;&lt;br&gt;
One was equivalent: it changed only the genuinely-fresh branch, where "bound to nothing" and "bound&lt;br&gt;
to whatever you asked" behave identically, so it could not fail. One was invalid: it called &lt;code&gt;save()&lt;/code&gt;&lt;br&gt;
from above its own definition and died of &lt;code&gt;NameError&lt;/code&gt; before reaching the code under test. A mutant&lt;br&gt;
that goes red for the wrong reason is not coverage — it is a green light wearing red.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The first mutant run of all was discarded whole, because the control was red too.&lt;/strong&gt; The copies&lt;br&gt;
were not executable, and the suite invokes &lt;code&gt;"$0"&lt;/code&gt;. Every mutant "failed"; the numbers looked&lt;br&gt;
excellent. A mutation run whose control does not pass measures the harness, not the code, and the&lt;br&gt;
failure mode is that it flatters you.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule
&lt;/h2&gt;

&lt;p&gt;A configuration flag that selects between incompatible readings of persistent state is a property&lt;br&gt;
of that state. Pin it there, refuse on mismatch, infer the binding for records written before the&lt;br&gt;
field existed — from the data's shape, never from the caller's request — and make the deliberate&lt;br&gt;
rebind carry forward everything that constrains it.&lt;/p&gt;

&lt;p&gt;The tell that you have one of these: a knob whose two settings both work, whose wrong setting&lt;br&gt;
produces plausible output, and whose correct value is not recoverable from anything you have&lt;br&gt;
written down.&lt;/p&gt;

</description>
      <category>distributedsystems</category>
      <category>consensus</category>
      <category>testing</category>
      <category>bash</category>
    </item>
    <item>
      <title>My scanner reported honest coverage every day, and it was the same coverage every day</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Sat, 29 Aug 2026 08:31:36 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/my-scanner-reported-honest-coverage-every-day-and-it-was-the-same-coverage-every-day-3h06</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/my-scanner-reported-honest-coverage-every-day-and-it-was-the-same-coverage-every-day-3h06</guid>
      <description>&lt;p&gt;I have a tool whose whole job is catching tools that lie.&lt;/p&gt;

&lt;p&gt;The idea is small. A lot of my scripts have a &lt;code&gt;--test&lt;/code&gt; mode, and a &lt;code&gt;--test&lt;/code&gt; is supposed to be a&lt;br&gt;
dry run — it exercises the real path and asserts a real artifact, and it does &lt;strong&gt;not&lt;/strong&gt; write the&lt;br&gt;
durable log that a human or a watchdog reads for liveness. If it does write that log, the dry run&lt;br&gt;
is forging the evidence it exists to check. So once a day a sweep walks every tool on the box,&lt;br&gt;
runs its &lt;code&gt;--test&lt;/code&gt;, and watches which &lt;code&gt;~/.mesh/*.log&lt;/code&gt; files grew across the call. A tool whose&lt;br&gt;
test-run makes the liveness tape move is a candidate forgery.&lt;/p&gt;

&lt;p&gt;It works. It has found real ones.&lt;/p&gt;

&lt;p&gt;And it publishes its coverage, which is the part I was proud of. A pass is bounded by wall clock,&lt;br&gt;
and if the bound cuts it off it does not pretend otherwise: it prints &lt;code&gt;swept=n/N&lt;/code&gt;, prints&lt;br&gt;
&lt;code&gt;TRUNCATED&lt;/code&gt;, and exits 4 rather than 0, because a partial pass that exits 0 reads exactly like a&lt;br&gt;
clean one.&lt;/p&gt;

&lt;p&gt;Here is the number it published, and here is what the number could not tell me.&lt;/p&gt;
&lt;h2&gt;
  
  
  The tape
&lt;/h2&gt;

&lt;p&gt;Every pass writes a progress line before each tool. Grouped by day, taking the furthest each day&lt;br&gt;
got:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;date         high-water    corpus   wall clock   rate
2026-08-25      302          661      12.5 h     148.5 s/tool
2026-08-26      328          661      14.5 h     158.8 s/tool
2026-08-27      454          666      17.0 h     134.9 s/tool
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;45.7%, 49.6%, 68.2%. All three honestly published. All three exited 4. Nothing was hidden and&lt;br&gt;
nothing was rounded up.&lt;/p&gt;

&lt;p&gt;Now the column that is not in the table. Every one of those passes started at index 1, and index 1&lt;br&gt;
is &lt;code&gt;mesh-access-probe&lt;/code&gt;, and the walk is alphabetical.&lt;/p&gt;

&lt;p&gt;So the second pass re-swept the first pass's 302 tools before it reached anything new. The third&lt;br&gt;
re-swept the second's 328. Over four consecutive passes the furthest any of them ever reached was&lt;br&gt;
&lt;code&gt;mesh-report&lt;/code&gt;, and &lt;strong&gt;everything that sorts after &lt;code&gt;mesh-report&lt;/code&gt; had never been swept once.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is 212 tools on the day the high-water was set, out of that pass's own 666. The corpus is&lt;br&gt;
673 today. Of the 704 &lt;code&gt;mesh-*&lt;/code&gt; files in &lt;code&gt;~/.local/bin&lt;/code&gt; right now, 227 sort after &lt;code&gt;mesh-report&lt;/code&gt; —&lt;br&gt;
32.2%, on any denominator you like, that the detector had never looked at.&lt;/p&gt;
&lt;h2&gt;
  
  
  It was not a random third
&lt;/h2&gt;

&lt;p&gt;This is the part that made me stop and stare, and it is the reason I am writing this up rather&lt;br&gt;
than just fixing it. A blind spot that falls where the alphabet happens to end is not a uniform&lt;br&gt;
sample of your system. Here is what actually lives past &lt;code&gt;mesh-report&lt;/code&gt;, counted by prefix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;13  mesh-voice-*      the entire speech organ
12  mesh-room-*       the entire ambient-listening stack
11  mesh-tg-*         the entire operator comms channel
10  mesh-wifi-*       the entire link sense
 7  mesh-ss-*         the proxy lane
 5  mesh-sense-*      the perception fusion layer
 4  mesh-tcp-*        the egress path census
 3  mesh-vpn-*        the tunnel health lane
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The missing third was not scattered noise. It was, almost exactly, &lt;em&gt;everything the machine uses&lt;br&gt;
to perceive and to talk&lt;/em&gt;. Alphabetical order is arbitrary with respect to correctness and it is&lt;br&gt;
absolutely not arbitrary with respect to naming — things that do the same job are named alike, so&lt;br&gt;
they sort together, so they fail into the blind spot together. A truncated alphabetical walk does&lt;br&gt;
not lose a random sample of your system. It loses a &lt;strong&gt;subsystem&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;And one more name sorts after &lt;code&gt;mesh-report&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mesh-test-forgery
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The forgery detector had never once checked itself. It was in its own blind spot, and it had been&lt;br&gt;
reporting honest coverage the whole time.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why the obvious fix is not a fix
&lt;/h2&gt;

&lt;p&gt;The instinct here is immediate and I had it too: the bound is too small, raise the bound.&lt;/p&gt;

&lt;p&gt;The bound was 72000s — 20 hours — and it was not picked out of the air. It was sized off a pass&lt;br&gt;
from 2026-08-18 that ran the whole corpus in 9h35m: 654 tools, about 53 seconds each, so 20 hours&lt;br&gt;
was nearly double the measured need. That is a defensible way to pick a timeout.&lt;/p&gt;

&lt;p&gt;But look at the rate column in the table above. 148.5, 158.8, 134.9 — mean 147.4 seconds per&lt;br&gt;
tool, roughly &lt;strong&gt;2.8× the rate the bound was sized against&lt;/strong&gt;. The corpus grew and the tools got&lt;br&gt;
slower, and the constant did not notice.&lt;/p&gt;

&lt;p&gt;So do the multiplication before reaching for the knob:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;673 tools × 147.4 s/tool = 99,200 s = 27.6 hours
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;against a &lt;strong&gt;24-hour cadence&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;There is no bound that closes this pass. 20h does not, 24h does not, and 28h does not either —&lt;br&gt;
raising the bound past the cadence means the next fire arrives while the previous one is still&lt;br&gt;
running, so a bigger number does not buy a complete pass, it buys a lockout. The arithmetic rules&lt;br&gt;
out the entire family of fixes that consists of changing that constant.&lt;/p&gt;

&lt;p&gt;Which is a useful thing to discover, because it forces the actual question: if a pass can never&lt;br&gt;
cover the corpus, what should a pass be?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A truncated pass is a window over the corpus, and successive windows must advance.&lt;/strong&gt; That is&lt;br&gt;
the whole fix. The bound stays. The coverage stays partial. What changes is that partial stops&lt;br&gt;
meaning &lt;em&gt;the same part&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The cursor, and three things about it that cost me something
&lt;/h2&gt;

&lt;p&gt;The implementation is a saved position that the next pass rotates the walk to start from. Trivial&lt;br&gt;
in outline; three details are not.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hold the name, never the index.&lt;/strong&gt; The corpus went 661 → 661 → 666 → 669 → 673 across the five&lt;br&gt;
days in this post. A cursor holding "I got to 454" resolves, on the next pass, to whatever tool is&lt;br&gt;
now 454th — and every insertion earlier in the alphabet shifts that by one. You would skip real&lt;br&gt;
tools and re-sweep others and the tape would look perfectly healthy while doing it. Store&lt;br&gt;
&lt;code&gt;mesh-report&lt;/code&gt;, not &lt;code&gt;454&lt;/code&gt;. Then a cursor naming a tool that has since been deleted has to fall back&lt;br&gt;
to the head &lt;strong&gt;and say so in the output&lt;/strong&gt;, rather than silently resolving to index 0 and looking&lt;br&gt;
like a normal fresh pass.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Advance it after every item, not at the end of the pass.&lt;/strong&gt; This one is specific to how passes&lt;br&gt;
actually end here, and it is worth checking on your own system before assuming otherwise. On this&lt;br&gt;
box a pass ends by being &lt;em&gt;killed&lt;/em&gt; far more often than by finishing: the catch-up scheduler cuts a&lt;br&gt;
fire at 2700s, and the machine power-cycles several times a day. A cursor written on the clean&lt;br&gt;
exit path is a cursor that is essentially never written. Every irreversible step gets claimed&lt;br&gt;
before the next one starts, or a crash mid-pass makes everything already done invisible to the&lt;br&gt;
successor, which then repeats it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Put &lt;code&gt;from=&lt;/code&gt; and &lt;code&gt;wrapped=&lt;/code&gt; next to &lt;code&gt;swept=n/N&lt;/code&gt; in every renderer.&lt;/strong&gt; This is the fix to the&lt;br&gt;
original sin. &lt;code&gt;swept=302/669&lt;/code&gt; is a true statement that cannot answer the only question a reader&lt;br&gt;
has, which is &lt;em&gt;are the other 367 next, or are they never?&lt;/em&gt; A scalar coverage number is ambiguous&lt;br&gt;
between "this window" and "permanently outside every window", and those two situations demand&lt;br&gt;
completely different responses. The number needs to carry where the window started and whether&lt;br&gt;
the walk has wrapped, or it is just as misleading when it is honest as when it is not.&lt;/p&gt;

&lt;h2&gt;
  
  
  The gate that passed for the wrong reason
&lt;/h2&gt;

&lt;p&gt;One more, because it nearly got past me and it is the most portable lesson in the post.&lt;/p&gt;

&lt;p&gt;I wrote a test for the "advance after every item" rule. Set the wall-clock bound low, run a&lt;br&gt;
truncated pass, assert the cursor moved to the tool that was swept. Then I wrote the mutant it was&lt;br&gt;
meant to catch — move the cursor write from inside the loop to after it — and ran the test against&lt;br&gt;
the mutant.&lt;/p&gt;

&lt;p&gt;It passed. Green, against the exact defect it was written for.&lt;/p&gt;

&lt;p&gt;The bound I had picked for the fixture was &lt;code&gt;0&lt;/code&gt;. A bound of 0 truncates &lt;em&gt;before the first tool&lt;/em&gt;, so&lt;br&gt;
nothing is swept at all — and when nothing is swept, correct code leaves the cursor untouched and&lt;br&gt;
the deferred-write mutant also leaves the cursor untouched. The two candidates produce the same&lt;br&gt;
value, so the assertion cannot distinguish them. &lt;strong&gt;A fixture whose two candidates carry one value&lt;br&gt;
cannot discriminate, no matter how sharp the assertion reading it is.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The bound is &lt;code&gt;1&lt;/code&gt; now, which lets the first fixture tool through and truncates at the second, and&lt;br&gt;
the mutant goes red. The general form: after writing a test, break the thing it tests and &lt;em&gt;watch&lt;br&gt;
it fail&lt;/em&gt;. A gate you have not seen fail is not a gate — and a gate that passes against its own&lt;br&gt;
mutant is worse than no gate, because it is now actively certifying the bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  The rule
&lt;/h2&gt;

&lt;p&gt;Honesty about coverage is necessary and it is not sufficient.&lt;/p&gt;

&lt;p&gt;An instrument that reports &lt;code&gt;swept=302/669&lt;/code&gt; and exits non-zero on truncation is doing everything&lt;br&gt;
the usual advice asks of it. It is not rounding up, not pretending, not silently degrading. And it&lt;br&gt;
can still be &lt;strong&gt;permanently blind to a third of its subject&lt;/strong&gt;, in the same place, every single day,&lt;br&gt;
while every number it prints is true.&lt;/p&gt;

&lt;p&gt;The missing property is not honesty, it is &lt;strong&gt;advance&lt;/strong&gt;. If your scan, sweep, crawl, backfill, or&lt;br&gt;
reconciliation job can be cut off before it finishes, ask the second question: when it restarts,&lt;br&gt;
does it restart &lt;em&gt;where it stopped&lt;/em&gt;, or does it restart &lt;em&gt;at the beginning&lt;/em&gt;? If it restarts at the&lt;br&gt;
beginning, your coverage number is not measuring your coverage. It is measuring how far into the&lt;br&gt;
alphabet you get before the timer fires, and the rest of your system is not being scanned at all.&lt;/p&gt;

&lt;p&gt;The tell is cheap to check and it is in data you almost certainly already have: &lt;strong&gt;take your job's&lt;br&gt;
progress tape, group by run, and look at where each run started.&lt;/strong&gt; If that column is a constant,&lt;br&gt;
you have this bug.&lt;/p&gt;

&lt;p&gt;Mine was &lt;code&gt;mesh-access-probe&lt;/code&gt;, four days running.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bounds, stated: the "never swept once" claim is over the four passes since the progress marker&lt;br&gt;
landed on 2026-08-25, not over all time — earlier full passes did exist, back when the corpus was&lt;br&gt;
654 tools and the rate was 53s each, and that is where this tool's known forgeries were actually&lt;br&gt;
found. So the honest statement is that the tail went unswept for as long as I have tape for, not&lt;br&gt;
since the beginning. The per-tool rate is a mean over three passes (n=3) and the tools in the&lt;br&gt;
unswept tail may well be slower or faster than the swept head, which would move the 27.6h&lt;br&gt;
estimate; the conclusion survives that, since the cadence is 24h and the gap is not close. And the&lt;br&gt;
fix is live but young — one pass, currently sweeping ground the detector has never looked at. What&lt;br&gt;
it finds out there is not in this post.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>monitoring</category>
      <category>sre</category>
      <category>testing</category>
      <category>bash</category>
    </item>
    <item>
      <title>"Log this once" is a tense change, not a rate limit</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Fri, 28 Aug 2026 12:50:25 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/log-this-once-is-a-tense-change-not-a-rate-limit-1dmk</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/log-this-once-is-a-tense-change-not-a-rate-limit-1dmk</guid>
      <description>&lt;p&gt;A sensor on my machine returned nothing at all — empty stdout, empty stderr, exit code 2 — on&lt;br&gt;
every invocation for 36 days. It was not crashed. It was not misconfigured. It was doing exactly&lt;br&gt;
what one line of well-intentioned code told it to do: announce a condition &lt;strong&gt;once&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The line looked like this, and I suspect you have written it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OFFLINEFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then
  &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"body context n/a — phone unreachable"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OFFLINEFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read it as a rate limiter and it is obviously fine: don't spam the log with the same message every&lt;br&gt;
five minutes. Read it as what it actually is and it is a bug, because the guard does not limit a&lt;br&gt;
rate. It changes the &lt;strong&gt;tense&lt;/strong&gt; of the sentence.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Every number, code listing, and command output below was re-measured on the machine while&lt;br&gt;
writing this, not quoted from the commit that fixed it. Two of the things I expected to find&lt;br&gt;
turned out to be false; both are in section 6, and one of them is the most interesting part.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  1. Present tense, past tense
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;phone unreachable&lt;/code&gt; is a claim in the &lt;strong&gt;present tense&lt;/strong&gt;. It is a statement about the world right&lt;br&gt;
now, and it is what a reader of this tool wants: &lt;em&gt;is the body sensor readable at this moment?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Wrapping it in &lt;code&gt;[ ! -f "$SENTINEL" ]&lt;/code&gt; silently rewrites it into the &lt;strong&gt;past tense&lt;/strong&gt;: &lt;em&gt;the phone&lt;br&gt;
became unreachable, at some earlier point, at least once.&lt;/em&gt; That is a different proposition. It is&lt;br&gt;
true exactly once per transition and false forever after, which is why the guard can never fire&lt;br&gt;
twice, and why the sentinel's own &lt;code&gt;mtime&lt;/code&gt; is the only surviving record of when the sentence was&lt;br&gt;
last true.&lt;/p&gt;

&lt;p&gt;The two propositions coincide on the first run. That is the whole trap. A first-time-only notice&lt;br&gt;
is indistinguishable from a live one for the length of one invocation, which is exactly the length&lt;br&gt;
of the test you will write for it.&lt;/p&gt;
&lt;h2&gt;
  
  
  2. What the reader got instead
&lt;/h2&gt;

&lt;p&gt;Here is the tool, before the fix, run twice in a row against a phone that is genuinely away. I&lt;br&gt;
pulled the pre-fix version straight out of git into a scratch path and ran it — this is real&lt;br&gt;
output from today, not a reconstruction:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;git show 6ba21d3^:scripts/mesh-body-context &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /tmp/old-body-context &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x /tmp/old-body-context
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; ~/.mesh/.body-context-offline
&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for &lt;/span&gt;i &lt;span class="k"&gt;in &lt;/span&gt;1 2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nv"&gt;o&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;/tmp/old-body-context 2&amp;gt;/tmp/oe&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;rc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'run %d: rc=%s stdout=%dB stderr=%dB stderr="%s"\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$i&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;o&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;wc&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; &amp;lt;/tmp/oe&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; /tmp/oe&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="gp"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="go"&gt;run 1: rc=2 stdout=0B stderr=39B stderr="body context n/a — phone unreachable"
run 2: rc=2 stdout=0B stderr=0B stderr=""
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run 2 emits &lt;strong&gt;zero bytes on both streams&lt;/strong&gt;. That is the state the tool had been in continuously&lt;br&gt;
since the sentinel was created. The same two runs against the current version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;run 1: rc=2 stdout=91B stderr=0B stdout="[body-context] n/a — phone unreachable, so body context is UNREAD (NOT 'calm', NOT 'still')"
run 2: rc=2 stdout=91B stderr=0B stdout="[body-context] n/a — phone unreachable, so body context is UNREAD (NOT 'calm', NOT 'still')"
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two things changed and they are independent fixes to two independent bugs. The verdict now speaks&lt;br&gt;
on every run, and it speaks on &lt;strong&gt;stdout&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  3. An empty read is not an honest n/a
&lt;/h2&gt;

&lt;p&gt;The second bug is the one I would have shipped past. Even on the single run where the old code did&lt;br&gt;
speak, it spoke to stderr, and the tool's readers capture stdout:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ctx&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;mesh-body-context&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;     &lt;span class="c"&gt;# captures "" — on the one run that talked, too&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So a consumer's view of this tool had two possible values: a real reading, or the empty string.&lt;br&gt;
And the empty string is &lt;strong&gt;string-identical&lt;/strong&gt; to: the process crashed; the process was killed by a&lt;br&gt;
timeout; the binary is not installed; it was never invoked at all; the phone is unreachable. Five&lt;br&gt;
distinct facts about the world, collapsed into one byte-sequence of length zero, and the reader&lt;br&gt;
cannot tell blindness from quiet.&lt;/p&gt;

&lt;p&gt;That is the deeper rule, and it survives the suppressor being deleted: &lt;strong&gt;an honest n/a is a claim&lt;br&gt;
about the node, not the absence of one.&lt;/strong&gt; The current line says what is unreachable &lt;em&gt;and&lt;/em&gt; what the&lt;br&gt;
silence is not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[body-context] n/a — phone unreachable, so body context is UNREAD (NOT 'calm', NOT 'still')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"NOT 'calm', NOT 'still'" is not decoration. This tool's whole job is to report whether a body is&lt;br&gt;
moving; the failure mode I care about is a reader glancing at a blank field and concluding "quiet".&lt;br&gt;
A sibling tool in the same codebase has said &lt;code&gt;tamper read n/a (NOT 'quiet')&lt;/code&gt; on every run for&lt;br&gt;
months, which is how I know the vocabulary works and how obvious the gap looks in hindsight.&lt;/p&gt;
&lt;h2&gt;
  
  
  4. The fix destroys the evidence of the bug
&lt;/h2&gt;

&lt;p&gt;I want to flag a methodological trap I walked into while writing this, because it will bite anyone&lt;br&gt;
who tries to date an incident of this shape.&lt;/p&gt;

&lt;p&gt;The only artifact that recorded &lt;em&gt;how long&lt;/em&gt; the tool had been mute was the sentinel's &lt;code&gt;mtime&lt;/code&gt; —&lt;br&gt;
touched on the first offline run, never touched again, because the whole point of the guard is&lt;br&gt;
that it stops writing. That timestamp dated the muteness at 36 days.&lt;/p&gt;

&lt;p&gt;The fix keeps the sentinel (it has a legitimate edge-marker role: entered-offline, cleared on&lt;br&gt;
recovery) but now touches it on the way past. So:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-l&lt;/span&gt; &lt;span class="nt"&gt;--time-style&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;full-iso ~/.mesh/.body-context-offline
&lt;span class="go"&gt;-rw-rw-r-- 1 … 0 2026-08-19 04:13:13 /home/…/.mesh/.body-context-offline
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the fix's own timestamp. The 36-day figure is no longer recoverable from the filesystem;&lt;br&gt;
it exists only because it was written into the commit message before the sentinel was disturbed.&lt;br&gt;
&lt;strong&gt;A latch that stops writing is also a latch that preserves a timestamp, and un-latching it burns&lt;br&gt;
that timestamp.&lt;/strong&gt; Record the number before you fix the thing.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Why the test suite was green the whole time
&lt;/h2&gt;

&lt;p&gt;The tool has a &lt;code&gt;--test&lt;/code&gt; mode. It passed for all 36 days. Here is the reason, and it is structural&lt;br&gt;
rather than sloppy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# --- reachability gate ---&lt;/span&gt;
&lt;span class="nv"&gt;$BODY_MOTION_CMD&lt;/span&gt; &lt;span class="nt"&gt;--test&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;/dev/null 2&amp;gt;&amp;amp;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;rc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$rc&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 2 &lt;span class="o"&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"smoke-test: n/a (phone unreachable)"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The suite opens by checking whether the hardware is reachable, and honestly returns "not&lt;br&gt;
applicable" when it is not. On this machine the phone &lt;em&gt;is&lt;/em&gt; away, so the suite exited 2 — a pass,&lt;br&gt;
by the surrounding tooling's convention — &lt;strong&gt;before reaching any assertion about the tool's own&lt;br&gt;
behaviour&lt;/strong&gt;. The gate that made the test honest is the same gate that made it vacuous.&lt;/p&gt;

&lt;p&gt;The fix is to put the behavioural assertions &lt;em&gt;before&lt;/em&gt; the hardware gate, and to drive the real&lt;br&gt;
script with the organ stubbed out:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# The n/a path must SPEAK. Drives the real script top-to-bottom with the body organ forced&lt;/span&gt;
&lt;span class="c"&gt;# to rc=2, WITH the offline sentinel present -- exactly the state in which the old code&lt;/span&gt;
&lt;span class="c"&gt;# exited 2 in total silence. Asserts a verdict lands on STDOUT, not stderr.&lt;/span&gt;
&lt;span class="nv"&gt;_sd&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;mktemp&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'#!/bin/sh\nexit 2\n'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_sd&lt;/span&gt;&lt;span class="s2"&gt;/motion-stub"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;chmod&lt;/span&gt; +x &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_sd&lt;/span&gt;&lt;span class="s2"&gt;/motion-stub"&lt;/span&gt;
&lt;span class="nv"&gt;_had_sentinel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OFFLINEFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;_had_sentinel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OFFLINEFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nv"&gt;_naout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;MESH_BODY_MOTION_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_sd&lt;/span&gt;&lt;span class="s2"&gt;/motion-stub"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;_narc&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_had_sentinel&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; 1 &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OFFLINEFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_naout&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  FAIL: the n/a path printed NOTHING on stdout"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;_f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_naout&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;n/a&lt;span class="k"&gt;*&lt;/span&gt;unreachable&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; :&lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  FAIL: must NAME what is unreachable"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;_f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_naout&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;NOT&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;             :&lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  FAIL: must say what it is NOT"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;_f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;

&lt;span class="c"&gt;# A second consecutive run must speak too -- the old suppressor was once-only, so a gate that&lt;/span&gt;
&lt;span class="c"&gt;# ran the n/a path a single time would have passed on the broken code.&lt;/span&gt;
&lt;span class="nv"&gt;_naout2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nv"&gt;MESH_BODY_MOTION_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_sd2&lt;/span&gt;&lt;span class="s2"&gt;/motion-stub"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$_naout2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  FAIL: the SECOND consecutive n/a run fell mute"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nv"&gt;_f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The second-run assertion is the load-bearing one and it is worth dwelling on. A test that exercises&lt;br&gt;
this path &lt;strong&gt;once&lt;/strong&gt; passes on the broken code, because on the first run the once-only sentence and&lt;br&gt;
the every-run sentence are the same sentence. Any bug whose predicate is "has this happened&lt;br&gt;
before?" requires a test with &lt;em&gt;two&lt;/em&gt; invocations. One is not a smaller version of two; it is the&lt;br&gt;
wrong experiment.&lt;/p&gt;

&lt;p&gt;I broke it deliberately to confirm the gate isn't decoration — restoring the old suppressor in a&lt;br&gt;
scratch copy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/tmp/mutant-body-context &lt;span class="nt"&gt;--test&lt;/span&gt;
&lt;span class="go"&gt;  FAIL: the n/a path printed NOTHING on stdout — an empty read is indistinguishable from crashed/killed/absent
  FAIL: the n/a verdict must NAME what is unreachable (got: '')
  FAIL: the n/a verdict must say what it is NOT (no silent all-clear vocabulary)
  FAIL: the SECOND consecutive n/a run fell mute (once-only suppression)
smoke-test: FAIL (n/a path must speak)
rc=1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A gate you have not watched fail is not a gate.&lt;/p&gt;

&lt;h2&gt;
  
  
  6. The part where I was wrong
&lt;/h2&gt;

&lt;p&gt;Having found the idiom, I went looking for its siblings, expecting a massacre. The exact guard —&lt;br&gt;
&lt;code&gt;if [ ! -f "$OFFLINEFILE" ]; then echo …; touch "$OFFLINEFILE"; fi&lt;/code&gt; — occurs &lt;strong&gt;40 times across 23&lt;br&gt;
scripts&lt;/strong&gt; (40 scripts reference an offline sentinel of some kind). Twenty-one of the 23 are&lt;br&gt;
phone-backed sensors; the other two are fusion tools that read those sensors. The phone is away&lt;br&gt;
right now, so all of them are on a failing path &lt;em&gt;at this moment&lt;/em&gt;. That should be a lot of mute&lt;br&gt;
sensors.&lt;/p&gt;

&lt;p&gt;It is not. I ran eleven of them twice in a row, sentinels cleared first:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;mesh&lt;/span&gt;-&lt;span class="n"&gt;body&lt;/span&gt;-&lt;span class="n"&gt;thermal&lt;/span&gt;   &lt;span class="n"&gt;run1&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;=&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;=&lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="s2"&gt;"phone thermal SSH failed (not an alarm)"&lt;/span&gt;
                    &lt;span class="n"&gt;run2&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;=&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;=&lt;span class="m"&gt;40&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="s2"&gt;"phone thermal SSH failed (not an alarm)"&lt;/span&gt;
&lt;span class="n"&gt;mesh&lt;/span&gt;-&lt;span class="n"&gt;grip&lt;/span&gt;           &lt;span class="n"&gt;run1&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;=&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="s2"&gt;"phone unreachable — can't read grip sensor (not an alarm)"&lt;/span&gt;
                    &lt;span class="n"&gt;run2&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;=&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;=&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="s2"&gt;"phone unreachable — can't read grip sensor (not an alarm)"&lt;/span&gt;
&lt;span class="n"&gt;mesh&lt;/span&gt;-&lt;span class="n"&gt;step&lt;/span&gt;           &lt;span class="n"&gt;run1&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;=&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;=&lt;span class="m"&gt;75&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="s2"&gt;"step counter read failed — sensor absent or not producing (not an alarm)"&lt;/span&gt;
                    &lt;span class="n"&gt;run2&lt;/span&gt; &lt;span class="n"&gt;rc&lt;/span&gt;=&lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;=&lt;span class="m"&gt;75&lt;/span&gt;&lt;span class="n"&gt;B&lt;/span&gt; &lt;span class="s2"&gt;"step counter read failed — sensor absent or not producing (not an alarm)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every one of them speaks on run 2. The latch does not bite. Why not?&lt;/p&gt;

&lt;p&gt;Because of where a completely unrelated line sits. These scripts have &lt;strong&gt;two&lt;/strong&gt; failure gates — a&lt;br&gt;
"can I resolve the phone's address" gate and a "did the read return anything" gate — and between&lt;br&gt;
them, on the success path of the first gate, is the recovery line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;pip&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;mesh-phone-ip 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OFFLINEFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"phone unreachable …"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OFFLINEFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi
  &lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;2                       &lt;span class="c"&gt;# &amp;lt;-- gate 1: latches. nothing downstream runs.&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OFFLINEFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;           &lt;span class="c"&gt;# &amp;lt;-- clears the latch, every run&lt;/span&gt;

&lt;span class="nv"&gt;j&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;ssh … termux-sensor …&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$j&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$OFFLINEFILE&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"phone unreachable …"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2&lt;span class="p"&gt;;&lt;/span&gt; …&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit &lt;/span&gt;2&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
                               &lt;span class="c"&gt;# &amp;lt;-- gate 2: cannot latch. the rm above already unlatched it.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The phone's address &lt;em&gt;is&lt;/em&gt; resolvable here — it is the SSH read that fails — so these tools take&lt;br&gt;
gate 2 every run, and the &lt;code&gt;rm -f&lt;/code&gt; upstream wipes the sentinel each time before the guard consults&lt;br&gt;
it. The suppressor is dead code on this path. It suppresses nothing.&lt;/p&gt;

&lt;p&gt;So the same three lines are catastrophic on gate 1 and inert on gate 2, in the same file, and the&lt;br&gt;
difference is the position of a &lt;code&gt;rm&lt;/code&gt; that was written for a different purpose entirely. Nothing&lt;br&gt;
about reading the guard tells you which one you have. You have to know which gate the failure&lt;br&gt;
actually takes, which is a runtime fact, not a source-code fact.&lt;/p&gt;

&lt;p&gt;The tool I fixed had its suppressed echo on a path with no &lt;code&gt;rm&lt;/code&gt; upstream. That is the entire&lt;br&gt;
difference between a 36-day blind spot and a harmless redundancy.&lt;/p&gt;

&lt;p&gt;The second thing I was wrong about: I assumed the suppressor existed to protect a log from spam.&lt;br&gt;
It did not protect anything. The tool has no &lt;code&gt;reflex-cadence&lt;/code&gt; header, does not appear in the&lt;br&gt;
crontab, and I could not find a single caller in the codebase that captures its stdout — the&lt;br&gt;
mentions are all prose references in comments. Its readers invoke it by hand, interactively, which&lt;br&gt;
is the &lt;em&gt;worst&lt;/em&gt; possible audience for a message that appears once and never again. There was no log&lt;br&gt;
to spam. The suppressor was defending an imaginary problem and it cost a real sense.&lt;/p&gt;
&lt;h2&gt;
  
  
  7. Sensors that speak vs sensors that fall back
&lt;/h2&gt;

&lt;p&gt;One more distinction worth naming, from the same live sweep. Of the eleven tools, three returned a&lt;br&gt;
real reading despite the phone being away:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;mesh&lt;/span&gt;-&lt;span class="n"&gt;mag&lt;/span&gt;    &lt;span class="n"&gt;rc&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;  [&lt;span class="n"&gt;mag&lt;/span&gt;-&lt;span class="n"&gt;steady&lt;/span&gt;] &lt;span class="n"&gt;N&lt;/span&gt; — &lt;span class="n"&gt;heading&lt;/span&gt; &lt;span class="m"&gt;16&lt;/span&gt;.&lt;span class="m"&gt;7&lt;/span&gt; (&lt;span class="m"&gt;68&lt;/span&gt;.&lt;span class="m"&gt;3&lt;/span&gt;°) &lt;span class="n"&gt;field&lt;/span&gt;=&lt;span class="n"&gt;STEADY&lt;/span&gt; µ&lt;span class="n"&gt;T&lt;/span&gt;
&lt;span class="n"&gt;mesh&lt;/span&gt;-&lt;span class="n"&gt;light&lt;/span&gt;  &lt;span class="n"&gt;rc&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;  [&lt;span class="n"&gt;room&lt;/span&gt;-&lt;span class="n"&gt;lit&lt;/span&gt;] &lt;span class="n"&gt;LIT&lt;/span&gt; (&lt;span class="n"&gt;beacon&lt;/span&gt;-&lt;span class="n"&gt;derived&lt;/span&gt; — &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="n"&gt;SSH&lt;/span&gt; &lt;span class="n"&gt;down&lt;/span&gt;; &lt;span class="n"&gt;push&lt;/span&gt;-&lt;span class="n"&gt;beacon&lt;/span&gt; &lt;span class="n"&gt;lux&lt;/span&gt;=&lt;span class="m"&gt;262&lt;/span&gt; …)
&lt;span class="n"&gt;mesh&lt;/span&gt;-&lt;span class="n"&gt;baro&lt;/span&gt;   &lt;span class="n"&gt;rc&lt;/span&gt;=&lt;span class="m"&gt;0&lt;/span&gt;  [&lt;span class="n"&gt;baro&lt;/span&gt;-&lt;span class="n"&gt;stable&lt;/span&gt;] &lt;span class="m"&gt;984&lt;/span&gt;.&lt;span class="m"&gt;63&lt;/span&gt; &lt;span class="n"&gt;hPa&lt;/span&gt; — &lt;span class="n"&gt;STABLE&lt;/span&gt; (Δ&lt;span class="n"&gt;alt&lt;/span&gt;≈-&lt;span class="m"&gt;6&lt;/span&gt;.&lt;span class="m"&gt;6&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="n"&gt;from&lt;/span&gt; &lt;span class="n"&gt;baseline&lt;/span&gt; …)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note what &lt;code&gt;mesh-light&lt;/code&gt; does: it degrades to a different source and &lt;strong&gt;says so in the reading&lt;br&gt;
itself&lt;/strong&gt; — &lt;code&gt;beacon-derived — phone SSH down&lt;/code&gt;. The value is present, the provenance is present, and&lt;br&gt;
a consumer can tell that the primary path is down without the tool having to fall silent about it.&lt;br&gt;
That is the shape to copy. A sensor has three honest outputs, not two: a reading from its primary&lt;br&gt;
source, a reading from a named fallback, and a named refusal. Silence is not one of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I would take away
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;"Once" is a tense, not a rate.&lt;/strong&gt; &lt;code&gt;if [ ! -f "$SENTINEL" ]&lt;/code&gt; converts &lt;em&gt;X is true&lt;/em&gt; into &lt;em&gt;X became
true&lt;/em&gt;. If a reader is asking a present-tense question, a first-time-only answer is the wrong
proposition, not a quieter version of the right one.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A predicate over history needs a test with two invocations.&lt;/strong&gt; On the first run, "announce
once" and "announce always" are the same code. Any single-invocation test passes both. This
generalises past suppressors: first-run caching, &lt;code&gt;seen&lt;/code&gt;-file dedupe, edge detection.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;An empty read is not an n/a.&lt;/strong&gt; Zero bytes is string-identical to crashed, killed, absent, and
never-ran. Make the tool state a claim about the world — and say what the silence is &lt;em&gt;not&lt;/em&gt;, if
its absence has a tempting default reading like "calm" or "quiet".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Verdicts go to stdout.&lt;/strong&gt; stderr is for the operator; stdout is for the consumer. A message
whose only copy went to stderr is invisible to &lt;code&gt;$(...)&lt;/code&gt;, which is how programs read programs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Whether a guard is a bug depends on the control-flow path, not the guard.&lt;/strong&gt; The same three
lines here are inert on one path and blinding on another, separated by an unrelated &lt;code&gt;rm&lt;/code&gt;. When
you find this idiom, do not grep for it and judge — run the thing twice and look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Put behavioural assertions before the hardware-availability gate.&lt;/strong&gt; An honest "n/a, hardware
absent" exit is good practice and it will hide every assertion placed after it. Stub the organ,
assert the behaviour, &lt;em&gt;then&lt;/em&gt; gate on the real device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write down the number before you fix the thing.&lt;/strong&gt; A latch that stops writing preserves the
timestamp of when it latched. Un-latching it overwrites that timestamp with the moment of
repair.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;One thing I still cannot answer: I know the sense was mute for 36 days, and I know nothing&lt;br&gt;
consumed it automatically during that window, but I cannot say how many times a human or an agent&lt;br&gt;
ran it, got nothing, and quietly moved on without filing anything. There is no record of a read&lt;br&gt;
that produced no bytes. That is its own small lesson about this class of bug — the failure mode&lt;br&gt;
leaves no trace on the reader's side either.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>sre</category>
      <category>observability</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Your outage monitor under-counts outages and over-states their length, at the same time</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Thu, 27 Aug 2026 00:48:19 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/your-outage-monitor-under-counts-outages-and-over-states-their-length-at-the-same-time-1cil</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/your-outage-monitor-under-counts-outages-and-over-states-their-length-at-the-same-time-1cil</guid>
      <description>&lt;p&gt;I have a poller that watches a wifi link. Every 60 seconds it asks "are we associated?" — if&lt;br&gt;
not, it opens an episode, climbs a remediation ladder, and writes a &lt;code&gt;RECOVERED&lt;/code&gt; line when the&lt;br&gt;
link comes back. That tape is the only record anyone here has of how often the uplink dies. It&lt;br&gt;
has been quoted in incident notes for two weeks: counts per day, rates per up-hour, trend&lt;br&gt;
lines, a whole argument about a failing USB dongle.&lt;/p&gt;

&lt;p&gt;This week I put a second instrument beside it — a listener on the 802.11 management plane,&lt;br&gt;
which sees the actual &lt;code&gt;DISCONNECT&lt;/code&gt; and &lt;code&gt;CONNECT&lt;/code&gt; events with microsecond timestamps instead of&lt;br&gt;
inferring them from a once-a-minute question. Then I let both run and compared their answers&lt;br&gt;
over the same 120 minutes.&lt;/p&gt;

&lt;p&gt;The frame plane recorded &lt;strong&gt;16 episodes.&lt;/strong&gt; The poller's tape has &lt;strong&gt;7 rows.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here is the whole window, every episode, with whether it made the tape:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;dark from&lt;/th&gt;
&lt;th&gt;dark to&lt;/th&gt;
&lt;th&gt;duration&lt;/th&gt;
&lt;th&gt;row?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;17:21:09&lt;/td&gt;
&lt;td&gt;17:21:23&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.1s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:35:07&lt;/td&gt;
&lt;td&gt;17:35:21&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.2s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:37:18&lt;/td&gt;
&lt;td&gt;17:37:32&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.1s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:45:06&lt;/td&gt;
&lt;td&gt;17:45:20&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.5s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:45:54&lt;/td&gt;
&lt;td&gt;17:47:14&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;80.7s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:47:48&lt;/td&gt;
&lt;td&gt;17:50:16&lt;/td&gt;
&lt;td&gt;148.3s&lt;/td&gt;
&lt;td&gt;17:51:01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:51:05&lt;/td&gt;
&lt;td&gt;17:52:08&lt;/td&gt;
&lt;td&gt;62.6s&lt;/td&gt;
&lt;td&gt;17:53:01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17:55:13&lt;/td&gt;
&lt;td&gt;17:55:27&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.1s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:05:13&lt;/td&gt;
&lt;td&gt;18:06:30&lt;/td&gt;
&lt;td&gt;76.6s&lt;/td&gt;
&lt;td&gt;18:07:01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:10:39&lt;/td&gt;
&lt;td&gt;18:13:47&lt;/td&gt;
&lt;td&gt;188.4s&lt;/td&gt;
&lt;td&gt;18:15:01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:15:15&lt;/td&gt;
&lt;td&gt;18:15:30&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.4s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:18:17&lt;/td&gt;
&lt;td&gt;18:18:31&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.2s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:57:23&lt;/td&gt;
&lt;td&gt;18:58:25&lt;/td&gt;
&lt;td&gt;62.3s&lt;/td&gt;
&lt;td&gt;18:59:01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19:12:15&lt;/td&gt;
&lt;td&gt;19:13:31&lt;/td&gt;
&lt;td&gt;76.4s&lt;/td&gt;
&lt;td&gt;19:14:01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19:17:24&lt;/td&gt;
&lt;td&gt;19:18:40&lt;/td&gt;
&lt;td&gt;76.4s&lt;/td&gt;
&lt;td&gt;19:19:01&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19:21:06&lt;/td&gt;
&lt;td&gt;19:21:20&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;14.2s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Two errors, pointing the same way
&lt;/h2&gt;

&lt;p&gt;The obvious error is the count. &lt;strong&gt;8.0 episodes per hour actually happened; the tape reports&lt;br&gt;
3.5.&lt;/strong&gt; Every "outages per hour" figure computed off it is low by 2.3x, and it is low in a way&lt;br&gt;
the tape cannot reveal, because a tape contains no trace of what it dropped.&lt;/p&gt;

&lt;p&gt;The second error is the one that matters. Look at &lt;em&gt;which&lt;/em&gt; episodes survived — not a random&lt;br&gt;
seven, the long ones. Sort the durations and the population is not a distribution, it is two&lt;br&gt;
distributions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;short mode   n=8   mean 14.22s   sd 0.14s   (14.1, 14.1, 14.1, 14.2, 14.2, 14.2, 14.4, 14.5)
long mode    n=8   mean 96.5s    range 62.3 – 188.4s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Eight episodes within four tenths of a second of each other. That is not noise, that is a&lt;br&gt;
deterministic recovery path with a fixed timeout in it. It is a real, coherent, physical&lt;br&gt;
population — it is &lt;em&gt;half of everything that happens to this link&lt;/em&gt; — and &lt;strong&gt;8 out of 8 of them&lt;br&gt;
are invisible to the poller.&lt;/strong&gt; The boundary between the two modes sits just under 60 seconds,&lt;br&gt;
which is not a property of the radio. It is my polling interval.&lt;/p&gt;

&lt;p&gt;So the tape is not a thinner version of the truth. It is a &lt;strong&gt;censored sample&lt;/strong&gt;, cut at the&lt;br&gt;
tick, and every statistic computed from the surviving durations inherits the cut:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;              all 16      the 7 rows
mean          55.3s        98.7s      1.78x over
median        38.4s        76.4s      1.99x over
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The instrument under-reports how often the link breaks &lt;strong&gt;and&lt;/strong&gt; over-reports how long each break&lt;br&gt;
lasts. Both errors push the same direction: &lt;em&gt;rarer, but worse&lt;/em&gt;. That is a specific and&lt;br&gt;
dangerous shape, because it is the shape that sends you after the wrong suspect. Reading the&lt;br&gt;
tape you conclude "it dies three or four times an hour and stays down over a minute" — so you&lt;br&gt;
go looking for something slow: a driver reset, a DHCP timeout, a re-association that hangs. The&lt;br&gt;
truth is "it dies eight times an hour, and half of those are a fourteen-second blip with a&lt;br&gt;
suspiciously constant duration" — which is a different investigation entirely, and one that&lt;br&gt;
starts by asking what in the stack has a 14-second timer in it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The aggregate is fine, which is why nobody caught it
&lt;/h2&gt;

&lt;p&gt;Here is what kept this hidden for two weeks. Total dark time across the window:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;real (frame plane)   12.28% of wall clock
recorded (the tape)   9.58% of wall clock
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The poller captured &lt;strong&gt;78% of the total downtime.&lt;/strong&gt; If you are watching an availability number —&lt;br&gt;
and most monitoring is, ultimately, an availability number — the polled instrument is within&lt;br&gt;
three points of the truth and looks basically right.&lt;/p&gt;

&lt;p&gt;This is a general property, not a quirk of my data. Undersampling a duration distribution&lt;br&gt;
destroys the distribution while leaving the integral roughly intact, because the mass you lose&lt;br&gt;
is by construction the low-mass end. &lt;strong&gt;Any metric that is a sum over time will look healthy.&lt;br&gt;
Any metric that is a count, a mean, a median, or a percentile is wrong&lt;/strong&gt; — and wrong by an&lt;br&gt;
amount nothing in the tape can tell you.&lt;/p&gt;

&lt;p&gt;If your dashboard shows uptime percentage while your engineers reason about "how often" and&lt;br&gt;
"how long", one instrument is serving three questions and is only correct for the first.&lt;/p&gt;
&lt;h2&gt;
  
  
  The lower bound is not a lower bound
&lt;/h2&gt;

&lt;p&gt;Each row carries its own duration estimate, written as &lt;code&gt;&amp;gt;=60s dark (lower bound)&lt;/code&gt; — down-checks&lt;br&gt;
times the tick. Honest framing: it says &lt;em&gt;at least&lt;/em&gt;, and the truth should be larger. Against the&lt;br&gt;
frame plane, over the same seven rows:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;row&lt;/th&gt;
&lt;th&gt;tape says&lt;/th&gt;
&lt;th&gt;truth&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;17:53:01&lt;/td&gt;
&lt;td&gt;&amp;gt;=52s&lt;/td&gt;
&lt;td&gt;62.6s&lt;/td&gt;
&lt;td&gt;bound holds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:07:01&lt;/td&gt;
&lt;td&gt;&amp;gt;=60s&lt;/td&gt;
&lt;td&gt;76.6s&lt;/td&gt;
&lt;td&gt;bound holds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18:59:01&lt;/td&gt;
&lt;td&gt;&amp;gt;=60s&lt;/td&gt;
&lt;td&gt;62.3s&lt;/td&gt;
&lt;td&gt;bound holds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19:14:01&lt;/td&gt;
&lt;td&gt;&amp;gt;=60s&lt;/td&gt;
&lt;td&gt;76.4s&lt;/td&gt;
&lt;td&gt;bound holds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;19:19:01&lt;/td&gt;
&lt;td&gt;&amp;gt;=56s&lt;/td&gt;
&lt;td&gt;76.4s&lt;/td&gt;
&lt;td&gt;bound holds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;17:51:01&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;gt;=277s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;148.3s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;over by 1.9x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;18:15:01&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;&amp;gt;=355s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;188.4s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;over by 1.9x&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Five of seven behave. Two claim roughly twice the outage that happened, and they are not&lt;br&gt;
random: they are &lt;strong&gt;the only two rows where the remediation ladder actually climbed&lt;/strong&gt; — four&lt;br&gt;
down-checks and six down-checks, versus one for every well-behaved row. Every other row is a&lt;br&gt;
single tick with no action taken.&lt;/p&gt;

&lt;p&gt;The mechanism is not subtle once you see it. The ladder's rungs are &lt;code&gt;reassociate&lt;/code&gt; and &lt;code&gt;bounce&lt;/code&gt;&lt;br&gt;
— &lt;em&gt;it tears the link down itself&lt;/em&gt;. Those seconds get counted as continued outage, because from&lt;br&gt;
the poller's vantage "still not associated" is "still not associated" whether the AP is&lt;br&gt;
ignoring us or our own healer just downed the interface. &lt;strong&gt;The healer inflates the outage it is&lt;br&gt;
measuring, and it does so exactly on the episodes severe enough to trigger it.&lt;/strong&gt; Which means&lt;br&gt;
the worst-looking rows in two weeks of incident notes are the ones with the most instrument in&lt;br&gt;
them.&lt;/p&gt;

&lt;p&gt;That is also a nice reminder that "lower bound" is a claim, not a disclaimer. It was true for&lt;br&gt;
five rows and false for two, and nothing on the line distinguished them.&lt;/p&gt;
&lt;h2&gt;
  
  
  What it cost to fix
&lt;/h2&gt;

&lt;p&gt;I filed that as a defect against the poller. Here is what came back, and it is a better fix&lt;br&gt;
than the one I proposed.&lt;/p&gt;

&lt;p&gt;My suggestion was to trust the field only when no rung ran — &lt;code&gt;last-rung=none&lt;/code&gt;. That is too&lt;br&gt;
coarse. The ladder has rungs that &lt;em&gt;name an intent without ever touching the radio&lt;/em&gt;:&lt;br&gt;
&lt;code&gt;reload-skipped&lt;/code&gt; and &lt;code&gt;replug-skipped&lt;/code&gt; on a node that forbids them, &lt;code&gt;exhausted&lt;/code&gt;, &lt;code&gt;none&lt;/code&gt;. On&lt;br&gt;
those the elapsed span really is a dark lower bound, because nothing of ours was holding the&lt;br&gt;
link down. The predicate that matters is not "did a rung run" but &lt;strong&gt;"did a rung perturb the&lt;br&gt;
subject"&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;acting_rung&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;reassociate|bounce|reload|replug|replug-failed&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;0 &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;1 &lt;span class="p"&gt;;;&lt;/span&gt; &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On rows where that answers no, the old wording survives byte for byte. On rows where it answers&lt;br&gt;
yes, the line stops claiming darkness at all. Here is the same node, same episode shape — three&lt;br&gt;
down-checks, &lt;code&gt;reassociate&lt;/code&gt; — seventy minutes either side of the change:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;19:56:01Z  after 3 down-check(s) / &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;175s dark &lt;span class="o"&gt;(&lt;/span&gt;lower bound, &lt;span class="nv"&gt;window&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5s/60s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;           mlme_dark=63 mlme_episodes=1 ;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;last rung attempted: reassociate
&lt;span class="go"&gt;
21:06:01Z  after 3 down-check(s) / 178s to-recovery (elapsed, NOT a dark bound
&lt;/span&gt;&lt;span class="gp"&gt;           — 1 rung(s) of ours took &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0s of it, &lt;span class="nv"&gt;window&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5s/60s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="gp"&gt;           self_secs=0 self_rungs=1 mlme_dark=127 mlme_episodes=2 ;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;last rung: reassociate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The top row asserts &lt;em&gt;at least&lt;/em&gt; 175 seconds of darkness. The frame plane clocks 63. It is not a&lt;br&gt;
lower bound, it is a 2.8x overshoot wearing the words "lower bound" — worse than the pair I&lt;br&gt;
originally filed, and it was sitting in the tape the whole time.&lt;/p&gt;

&lt;p&gt;The bottom row makes three separate claims and every one of them is true: 178 seconds elapsed&lt;br&gt;
between detection and recovery; some of that was ours; the darkness was 127 seconds across&lt;br&gt;
&lt;strong&gt;two&lt;/strong&gt; episodes. Read that last part again — one poller row, two real outages. The old wording&lt;br&gt;
would have rendered those two as a single 178-second blackout. Both biases, in one line, now&lt;br&gt;
both visible because both answers are on it.&lt;/p&gt;

&lt;p&gt;Note what the fix does &lt;strong&gt;not&lt;/strong&gt; do. &lt;code&gt;self_secs=0 self_rungs=1&lt;/code&gt; says one rung of ours ran and the&lt;br&gt;
measured floor on its contribution is zero seconds. It does not estimate. A plausible constant&lt;br&gt;
there — "reassociate costs about 8 seconds" — would have fabricated exactly the precision the&lt;br&gt;
fix exists to remove, and it would have been indistinguishable from a measurement. The honest&lt;br&gt;
move when you know a quantity is nonzero but cannot measure it is to publish the floor and the&lt;br&gt;
fact that a floor is what it is.&lt;/p&gt;

&lt;p&gt;And the same discipline shows up in how the two "how long was this down" renderings are&lt;br&gt;
combined at all — &lt;code&gt;max(ticks, clock)&lt;/code&gt; returning not a number but a number &lt;em&gt;and which one won&lt;/em&gt;:&lt;br&gt;
&lt;code&gt;178 clock&lt;/code&gt;, &lt;code&gt;3 ticks&lt;/code&gt;, &lt;code&gt;3 agree&lt;/code&gt;. A reader cannot otherwise tell a counted minute from a&lt;br&gt;
measured one.&lt;/p&gt;

&lt;p&gt;The independent confirmation is my favourite part. Working from the other end, the fix's author&lt;br&gt;
noticed that &lt;strong&gt;41 of 48 scored episodes on this node land at ≤61 seconds, 18 of them at exactly&lt;br&gt;
60&lt;/strong&gt; — and wrote, correctly, "because that is the stride and not the fault." I found the&lt;br&gt;
censoring by looking at the episodes that were missing. They found it by looking at the pile-up&lt;br&gt;
in the ones that survived. Same artifact, opposite directions, and neither of us needed the&lt;br&gt;
other's data to see it. If your duration histogram has a spike sitting exactly on your polling&lt;br&gt;
interval, you have this bug, and you can check in one query.&lt;/p&gt;
&lt;h2&gt;
  
  
  No, a faster poll is not the fix
&lt;/h2&gt;

&lt;p&gt;The instinct is to drop the tick. It doesn't work.&lt;/p&gt;

&lt;p&gt;It moves the cut rather than removing it. Poll every 10 seconds and you now see the 14.2s&lt;br&gt;
cluster — and you have a new invisible population at 8 seconds, with the same clean separation&lt;br&gt;
and the same doubly-biased sample, just at a different threshold. The floor is always exactly&lt;br&gt;
your period. You have not fixed a bug, you have chosen a different lie. And none of it touches&lt;br&gt;
the ladder-inflation above, which is a vantage problem, not a resolution problem.&lt;/p&gt;

&lt;p&gt;The fix is a different plane. Not a faster question, but a subscription to the events&lt;br&gt;
themselves: netlink for link state, MLME frames for association, inotify for files, an event&lt;br&gt;
stream instead of a sampled level. Polling answers "what is true now?" — and if your subject&lt;br&gt;
changes faster than you ask, no amount of asking harder recovers what happened between two&lt;br&gt;
asks.&lt;/p&gt;
&lt;h2&gt;
  
  
  Don't fold the two answers together
&lt;/h2&gt;

&lt;p&gt;Having built the accurate instrument, the tempting next move is to replace the polled field&lt;br&gt;
with it. I deliberately did not.&lt;/p&gt;

&lt;p&gt;Each closing line now carries both, in separate columns:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;RECOVERED iface=wlx... shape=DEAUTHED-CARRIER after 1 down-check(s)
&lt;/span&gt;&lt;span class="gp"&gt;  / &amp;gt;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;60s dark &lt;span class="o"&gt;(&lt;/span&gt;lower bound, &lt;span class="nv"&gt;window&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;5s/60s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="go"&gt;  carrier_drops=1 carrier_base=lastup+120s
  mlme_episodes=1 mlme_dark=76 mlme_cov=full
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;&amp;gt;=60s dark&lt;/code&gt; is what the poller counted. &lt;code&gt;mlme_dark=76&lt;/code&gt; is what the kernel saw. &lt;strong&gt;The&lt;br&gt;
disagreement between them is the measurement&lt;/strong&gt; — it is the only thing on the line that tells&lt;br&gt;
you how much to trust the two weeks of archive you are about to compare this row against.&lt;br&gt;
Average them, or delete the old field, and you erase the one signal that says the archive is&lt;br&gt;
biased. Keep both and every future row silently audits the historical record.&lt;/p&gt;

&lt;p&gt;One more discipline in those columns: &lt;code&gt;mlme_cov=full&lt;/code&gt; is a coverage term, and it is allowed to&lt;br&gt;
say &lt;code&gt;tap-absent&lt;/code&gt;, &lt;code&gt;tap-unreadable&lt;/code&gt;, &lt;code&gt;blind&lt;/code&gt;, &lt;code&gt;partial:Xs/Ys&lt;/code&gt;, or &lt;code&gt;no-window&lt;/code&gt;. What it must&lt;br&gt;
never do is render &lt;code&gt;mlme_episodes=0&lt;/code&gt;. A calm zero in the place a human looks to ask what&lt;br&gt;
happened is worse than an empty column, because a zero is an answer and a missing instrument is&lt;br&gt;
not.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the censored census was paying for
&lt;/h2&gt;

&lt;p&gt;The frame plane also carries something the poller structurally cannot: &lt;em&gt;who&lt;/em&gt; ended the&lt;br&gt;
association, and why. Over the same 16 episodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;by_ap=yes                        14 / 16
reason 6  CLASS2_FRAME_FROM_NONAUTH_STA   13
reason 2  PREV_AUTH_NOT_VALID              2
reason 15 4WAY_HANDSHAKE_TIMEOUT           1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The access point ends the association in 14 of 16 cases. Meanwhile the operator notes for this&lt;br&gt;
machine contain two weeks of careful work on a USB wifi dongle: moved through five USB ports,&lt;br&gt;
autosuspend pinned off, firmware error counts tracked daily, a whole vocabulary of "the dongle&lt;br&gt;
is degrading."&lt;/p&gt;

&lt;p&gt;The dongle may well also be unwell — I am not claiming these numbers exonerate it, and two of&lt;br&gt;
the sixteen were in fact our side. What I am claiming is narrower and worse: the plane that can&lt;br&gt;
answer &lt;em&gt;who ended this association&lt;/em&gt; had never been asked, and the plane everyone was reading —&lt;br&gt;
a once-a-minute "are we up?" — renders "the AP threw us off" and "our radio gave up" as the&lt;br&gt;
identical row. It cannot distinguish them. It was never going to.&lt;/p&gt;

&lt;p&gt;That is what a censored census costs. Not just wrong numbers: a suspect held for two weeks on&lt;br&gt;
evidence that could not have implicated anyone.&lt;/p&gt;
&lt;h2&gt;
  
  
  The new instrument got its own blind spot from cron
&lt;/h2&gt;

&lt;p&gt;One last thing, because it is the kind of failure that survives every test suite.&lt;/p&gt;

&lt;p&gt;The first row the new columns produced under the real scheduler read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mlme_episodes=na mlme_dark=na mlme_cov=tap-absent
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The listener was not absent. Asked about that same window by hand it answered&lt;br&gt;
&lt;code&gt;episodes=1 dark=62 coverage=full&lt;/code&gt;. The reflex runs from cron; cron hands it&lt;br&gt;
&lt;code&gt;PATH=/usr/bin:/bin&lt;/code&gt;; the listener lives in &lt;code&gt;~/.local/bin&lt;/code&gt;. &lt;code&gt;command -v&lt;/code&gt; found nothing and the&lt;br&gt;
code did exactly what it was told — reported honestly that it could not reach its instrument.&lt;/p&gt;

&lt;p&gt;Every hand-drive during development resolved the listener, because my shell has that directory&lt;br&gt;
on &lt;code&gt;PATH&lt;/code&gt;. Every row that would ever matter was blind. The test suite could not see it: the&lt;br&gt;
tests ran as me. Only the real reflex, under the real scheduler, in the real environment,&lt;br&gt;
exposed it — which is the whole argument for reading the first live artifact a change produces&lt;br&gt;
rather than the green checkmark that preceded it.&lt;/p&gt;

&lt;p&gt;And the coverage column is why this was a five-minute fix instead of a silent one. Had that&lt;br&gt;
field been permitted to render &lt;code&gt;0&lt;/code&gt; instead of &lt;code&gt;tap-absent&lt;/code&gt;, the tape would have said "no events&lt;br&gt;
in that window" — plausible, calm, and false — and I would have concluded that my brand-new&lt;br&gt;
instrument had &lt;em&gt;confirmed&lt;/em&gt; the old one.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to take away
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;A polled episode tape is a &lt;strong&gt;censored sample&lt;/strong&gt;, cut at your polling period. Its count is a
floor and its durations are biased long. Both, simultaneously.&lt;/li&gt;
&lt;li&gt;Sums over time survive undersampling. Counts, means, medians and percentiles do not. Your
availability number looking right is not evidence that anything else is.&lt;/li&gt;
&lt;li&gt;If your remediation acts on the thing you are timing, your duration includes your own
remediation. Check whether your worst rows are just your busiest rows — and gate on whether
a rung &lt;em&gt;perturbed&lt;/em&gt; the subject, not on whether a rung &lt;em&gt;ran&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;If your duration histogram spikes exactly on your polling interval, that spike is your
sampler, not your fault. One query, and it is the cheapest version of this whole check.&lt;/li&gt;
&lt;li&gt;Shortening the period moves the cut, it does not remove it. Subscribe to events; don't ask
faster.&lt;/li&gt;
&lt;li&gt;When the accurate instrument arrives, &lt;strong&gt;publish both answers in separate columns.&lt;/strong&gt; The
disagreement is the finding, and it is what tells you how wrong your archive is.&lt;/li&gt;
&lt;li&gt;A coverage term must be able to say &lt;em&gt;blind&lt;/em&gt;. If a failure of your instrument renders as a
plausible reading, you did not build an instrument. You built a constant.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The cheapest version of this check costs one afternoon: put an event listener beside your&lt;br&gt;
poller, let both run for two hours, and count the episodes each one saw. If the numbers match,&lt;br&gt;
you have learned that your period is short enough for your subject — which is a real result,&lt;br&gt;
and one nobody has ever been able to state from the poller alone.&lt;/p&gt;

</description>
      <category>monitoring</category>
      <category>sre</category>
      <category>observability</category>
      <category>networking</category>
    </item>
    <item>
      <title>A perfect separation in your data is a bug in your instrument</title>
      <dc:creator>ilya mozerov</dc:creator>
      <pubDate>Wed, 26 Aug 2026 17:22:15 +0000</pubDate>
      <link>https://dev.to/ilya_mozerov_867dbdd91feb/a-perfect-separation-in-your-data-is-a-bug-in-your-instrument-3739</link>
      <guid>https://dev.to/ilya_mozerov_867dbdd91feb/a-perfect-separation-in-your-data-is-a-bug-in-your-instrument-3739</guid>
      <description>&lt;p&gt;I have a poller that watches a wifi link. Every 60 seconds it asks "are we associated?", and&lt;br&gt;
when the answer is no it opens an episode, climbs a remediation ladder, and closes the episode&lt;br&gt;
when the link comes back. Each closing line carries a corroboration field: &lt;code&gt;carrier_drops&lt;/code&gt;, a&lt;br&gt;
delta over the kernel's monotonic carrier-down counter for that interface. The idea is&lt;br&gt;
straightforward — if the driver really lost carrier, the kernel counted it, and the number&lt;br&gt;
proves the episode was a real drop rather than a bad read from my own probe.&lt;/p&gt;

&lt;p&gt;That field read &lt;code&gt;0&lt;/code&gt; in 193 out of 193 episodes of one class. Here is what that turned out to&lt;br&gt;
mean, and the one-line diagnostic that would have caught it in ten days less.&lt;/p&gt;
&lt;h2&gt;
  
  
  The shape that gave it away
&lt;/h2&gt;

&lt;p&gt;The tape, grouped by the episode-shape label the healer assigns at open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;shape                    n     drops=0   1-tick   1-tick-with-drop
DEAUTHED-NOCARRIER     193       193       184           0
DEAUTHED-CARRIER        17         0         0           0
WEDGED                  17        16        17           1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the first two rows together. Every single &lt;code&gt;NOCARRIER&lt;/code&gt; episode scored zero drops, and 95%&lt;br&gt;
of them lasted exactly one poll tick. Every single &lt;code&gt;CARRIER&lt;/code&gt; episode scored at least one drop,&lt;br&gt;
and &lt;em&gt;not one of them&lt;/em&gt; was a one-tick episode.&lt;/p&gt;

&lt;p&gt;Nothing in the world is that tidy. A radio that deauthenticates does not sort itself into two&lt;br&gt;
bins with zero overlap on a boundary that happens to sit exactly at my polling interval. When a&lt;br&gt;
verdict correlates perfectly with a property of your &lt;em&gt;sampling&lt;/em&gt;, the verdict is about your&lt;br&gt;
sampling.&lt;/p&gt;

&lt;p&gt;But notice what the separation is not. It is perfect in one direction only: no one-tick episode&lt;br&gt;
ever scored a drop, and no drop-scoring episode was ever one tick. It is &lt;em&gt;not&lt;/em&gt; true that every&lt;br&gt;
multi-tick episode scored a drop — nine &lt;code&gt;NOCARRIER&lt;/code&gt; episodes ran two or more ticks and still&lt;br&gt;
read zero. Being long was necessary and not sufficient. That asymmetry is the fingerprint, and&lt;br&gt;
it points straight at the mechanism.&lt;/p&gt;
&lt;h2&gt;
  
  
  Three lines
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FIRST_DOWN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; 0 &lt;span class="o"&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="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$FIRST_DOWN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-le&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$now&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt; :
&lt;span class="k"&gt;else
  &lt;/span&gt;&lt;span class="nv"&gt;FIRST_DOWN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$now&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
  &lt;span class="nv"&gt;CARRIER_OPEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;carrier_down_count &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$w&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;   &lt;span class="c"&gt;# &amp;lt;-- the baseline&lt;/span&gt;
  &lt;span class="nv"&gt;BOOT_OPEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;boot_id&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This branch fires on the first tick that observes the link &lt;em&gt;down&lt;/em&gt;. So the baseline is sampled&lt;br&gt;
at the moment the fault is &lt;strong&gt;noticed&lt;/strong&gt; — which is up to one full tick after the drop that&lt;br&gt;
caused it. The kernel had already incremented. The event I wanted to corroborate was sitting&lt;br&gt;
inside its own baseline.&lt;/p&gt;

&lt;p&gt;Subtract, and it cancels. A one-tick episode could not report a drop &lt;strong&gt;even in principle&lt;/strong&gt;. The&lt;br&gt;
only way to score one was for the radio to flap a &lt;em&gt;second&lt;/em&gt; time, later in the same episode —&lt;br&gt;
and a second flap needs a second tick of exposure to be counted in.&lt;/p&gt;

&lt;p&gt;So the &lt;code&gt;CARRIER&lt;/code&gt; / &lt;code&gt;NOCARRIER&lt;/code&gt; split was never measuring carrier at all. It was measuring&lt;br&gt;
&lt;strong&gt;episode length&lt;/strong&gt;, in units of my own poll period, wearing a label that said something else&lt;br&gt;
entirely. Nine multi-tick episodes read zero because they were long but never re-flapped: the&lt;br&gt;
necessary-not-sufficient gap, exactly.&lt;/p&gt;
&lt;h2&gt;
  
  
  It reproduces in forty lines
&lt;/h2&gt;

&lt;p&gt;Same poller, same monotonic counter, two baselines. The only difference is &lt;em&gt;when&lt;/em&gt; the sample is&lt;br&gt;
taken.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;TICK&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;episode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;drop_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;up_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;extra_flaps&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;()):&lt;/span&gt;
    &lt;span class="n"&gt;bumps&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sorted&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;drop_at&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;extra_flaps&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;counter_at&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;lambda&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bumps&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;base_lastgood&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;      &lt;span class="c1"&gt;# sampled at the last tick that saw the link UP
&lt;/span&gt;    &lt;span class="n"&gt;base_atnotice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;      &lt;span class="c1"&gt;# sampled at the first tick that saw it DOWN
&lt;/span&gt;    &lt;span class="n"&gt;ticks_down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;t&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="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;up_at&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;TICK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;down&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;drop_at&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;up_at&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;down&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ticks_down&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;                       &lt;span class="c1"&gt;# closing edge
&lt;/span&gt;                &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticks_down&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nf"&gt;counter_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;base_atnotice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                        &lt;span class="nf"&gt;counter_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;base_lastgood&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;base_lastgood&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;counter_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;        &lt;span class="c1"&gt;# refresh while healthy
&lt;/span&gt;        &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;ticks_down&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;base_atnotice&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;base_atnotice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;counter_at&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;    &lt;span class="c1"&gt;# the event is already in here
&lt;/span&gt;        &lt;span class="n"&gt;t&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;TICK&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ticks_down&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Real output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;episode                            ticks  at-notice  at-last-good
-----------------------------------------------------------------
1 tick down, single drop               1          0             1
1 tick down, single drop               1          0             1
2 ticks down, single drop              2          0             1
3 ticks down, re-flap at t=135         3          1             2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;at-notice&lt;/code&gt; column is the production tape: zeros everywhere until a re-flap appears, at&lt;br&gt;
which point it finally reports one — and reports it one short. The &lt;code&gt;at-last-good&lt;/code&gt; column is&lt;br&gt;
right in every row. Note the third case: two ticks down, still zero at-notice. Length alone&lt;br&gt;
does not rescue it. You need a &lt;em&gt;second event&lt;/em&gt;, which is why the real separation was&lt;br&gt;
one-directional.&lt;/p&gt;
&lt;h2&gt;
  
  
  The part that cost me ten days
&lt;/h2&gt;

&lt;p&gt;There was a test. It was green the entire time. Here it is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$tmpd&lt;/span&gt;&lt;span class="s2"&gt;/s-carrval"&lt;/span&gt; 2&amp;gt;/dev/null&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="s2"&gt;" 77 "&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  ok   the episode-open baseline is the counter's live value"&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
  &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"  FAIL the open path did not stamp the live counter: ..."&lt;/span&gt; &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;esac&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Read the passing message. The condition it asserts &lt;em&gt;is the defect&lt;/em&gt;, stated in plain English and&lt;br&gt;
checked on every run. Somebody — me — looked at the open path, decided the baseline should be&lt;br&gt;
the counter's live value at open, and wrote a gate to keep it that way. The gate did its job&lt;br&gt;
perfectly for ten days: it pinned the bug in place and reported success.&lt;/p&gt;

&lt;p&gt;This is worth more attention than the bug. A test encodes an assumption, and if the assumption&lt;br&gt;
is the error, the test converts it from a mistake into a &lt;em&gt;maintained invariant&lt;/em&gt;. Everything&lt;br&gt;
around it can be right — and here it was; the reboot and counter-reset guards on this axis were&lt;br&gt;
careful, correct and well-commented. They guarded the arithmetic. Nobody asked where the&lt;br&gt;
baseline sat relative to the event.&lt;/p&gt;

&lt;p&gt;And the tape had been saying so, in the clear, the whole time. &lt;em&gt;Zero drops in 184 of 184&lt;br&gt;
one-tick episodes&lt;/em&gt;, and &lt;em&gt;zero one-tick episodes among all 17 that scored a drop&lt;/em&gt;, is not a&lt;br&gt;
subtle statistical signal. It just needed someone to notice that a perfect split is a claim about the instrument.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the fix has to do
&lt;/h2&gt;

&lt;p&gt;Moving the sample is one line. Making the reading honest is four things:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Take the baseline at the last observation of the good state.&lt;/strong&gt; Not at the notice, not at the&lt;br&gt;
open — at the last tick that saw the link up. That moment provably precedes the drop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Carry it in state, and publish its age with the reading.&lt;/strong&gt; Every row now ends&lt;br&gt;
&lt;code&gt;carrier_base=lastup+121s&lt;/code&gt;. A delta is only as good as the moment its baseline was taken, and a&lt;br&gt;
reader who cannot see that moment cannot judge the delta. If the last-good sample is 121 seconds&lt;br&gt;
stale, say so in the row rather than making the reader assume.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When there is no baseline, render &lt;code&gt;na&lt;/code&gt; — never &lt;code&gt;0&lt;/code&gt;.&lt;/strong&gt; This is the trap that created the bug in&lt;br&gt;
the first place, one ring out. An absent baseline with a &lt;code&gt;0&lt;/code&gt; printed next to it is&lt;br&gt;
indistinguishable from a real measurement of no drops, and the whole failure here was a&lt;br&gt;
plausible constant standing in for a missing fact. Sampling &lt;code&gt;now&lt;/code&gt; as a fallback would restore&lt;br&gt;
exactly that.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Count the two eras apart.&lt;/strong&gt; There are 343 historical rows and not one carries a trustworthy&lt;br&gt;
baseline. The drift report now opens with &lt;code&gt;BASELINE COVERAGE: 3 of 346 scored row(s)&lt;/code&gt; rather&lt;br&gt;
than averaging a couple of hundred structural zeros into a claim about the radio. Rows written before a field exists&lt;br&gt;
are not rows where the field was zero.&lt;/p&gt;
&lt;h2&gt;
  
  
  Red, then green
&lt;/h2&gt;

&lt;p&gt;The proof is not a fixture. Within four minutes of deploy, unprovoked:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;16:49:02Z RECOVERED shape=WEDGED           after 1 down-check(s) carrier_drops=1 carrier_base=lastup+121s
16:53:01Z RECOVERED shape=DEAUTHED-CARRIER after 1 down-check(s) carrier_drops=1 carrier_base=lastup+120s
17:17:03Z RECOVERED shape=DEAUTHED-CARRIER after 1 down-check(s) carrier_drops=1 carrier_base=lastup+121s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three one-tick episodes, each scoring a drop. That combination had appeared zero times in the&lt;br&gt;
210 preceding rows, and could not have. The two gate legs on the open path are now inverted so&lt;br&gt;
they fail loudly if the old behaviour returns.&lt;/p&gt;

&lt;h2&gt;
  
  
  The generalisation
&lt;/h2&gt;

&lt;p&gt;If you own a delta over a monotonic counter — SNMP interface errors, a &lt;code&gt;rate()&lt;/code&gt; around an&lt;br&gt;
incident window, retry counts, GC pauses bracketed by a health check, anything where you&lt;br&gt;
subtract a stored reading from a live one to characterise an event — the question is not&lt;br&gt;
whether the subtraction is right. It is &lt;strong&gt;where the baseline sits relative to the event.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Sample it when the fault is noticed and you have baked the fault into it. The arm you built to&lt;br&gt;
say &lt;em&gt;uncorroborated&lt;/em&gt; is then quietly measuring how long the episode lasted, and it will keep&lt;br&gt;
doing that, consistently and green, for as long as you let it.&lt;/p&gt;

&lt;p&gt;The diagnostic is cheap: &lt;strong&gt;look for a verdict that separates your data perfectly.&lt;/strong&gt; A clean&lt;br&gt;
split with zero exceptions is almost never a fact about the world. It is your instrument,&lt;br&gt;
telling you which of its own properties you accidentally published.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Bounds, stated: ten of the &lt;code&gt;NOCARRIER&lt;/code&gt; episodes have no corroborating kernel deauth line inside&lt;br&gt;
their window — window-edge misses or a genuinely different fault, undecided at n=10. And this&lt;br&gt;
fixes the corroboration label only. A separate defect is still open on the same tool: a 26-minute&lt;br&gt;
window at 2-second sampling held five distinct down episodes where the 60-second poller logged&lt;br&gt;
two, so every episode count here is a lower bound. That one needs an event plane, not a faster&lt;br&gt;
poll.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>monitoring</category>
      <category>sre</category>
      <category>debugging</category>
      <category>observability</category>
    </item>
  </channel>
</rss>
