<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Zsh on Lessons learned</title><link>https://eghuro.cz/tags/zsh/</link><description>Recent content in Zsh on Lessons learned</description><generator>Hugo</generator><language>en-us</language><copyright>© Alexandr Mansurov · Licensed under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/)</copyright><lastBuildDate>Tue, 29 Sep 2026 12:05:00 +0200</lastBuildDate><atom:link href="https://eghuro.cz/tags/zsh/index.xml" rel="self" type="application/rss+xml"/><item><title>Zero failures is suspicious: agent checks that silently ran nothing</title><link>https://eghuro.cz/posts/zero-failures-is-suspicious/</link><pubDate>Tue, 29 Sep 2026 12:05:00 +0200</pubDate><guid>https://eghuro.cz/posts/zero-failures-is-suspicious/</guid><description>&lt;p&gt;My coding agent ran each of the two flaky Rust tests 150 times and got this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;streamed_read_right_after_a_rename: 0/150 failed&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;renames_are_combined_across_changes_they_do_not_depend_on: 0/150 failed&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Its next message was &amp;ldquo;Zero failures is suspicious.&amp;rdquo; It was right: neither test had run once. In the same week, three of its checks gave a tidy answer (nothing found, all passed, all failed) that came from a broken command, and each broken command hid its own error.&lt;/p&gt;&#10;&lt;p&gt;If your agent&amp;rsquo;s output has &lt;code&gt;(eval):log:1: too many arguments&lt;/code&gt;, &lt;code&gt;0 passed; 0 failed; … filtered out&lt;/code&gt; or &lt;code&gt;argument list too long&lt;/code&gt; somewhere above the summary, the summary is about a check that never ran.&lt;/p&gt;&#10;&#10;&lt;h2 id="the-setup"&gt;The setup &lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Claude Code 2.1.283 on macOS, where the agent&amp;rsquo;s shell is zsh 5.9&lt;/li&gt;&#10;&lt;li&gt;A Rust workspace whose test binary is run directly from &lt;code&gt;target/debug/deps&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&#10;&lt;h2 id="trap-1-zsh-has-a-log-builtin"&gt;Trap 1: zsh has a &lt;code&gt;log&lt;/code&gt; builtin &lt;/h2&gt;&#10;&lt;p&gt;The agent was debugging &lt;a&#10; href="https://eghuro.cz/posts/fskit-mount-fts-read-operation-not-permitted"&#10; &#10; data-rel="prefetch"&#10; &#10; &#10;&gt;an FSKit file system extension&lt;/a&gt;&#10; and searched the unified log for sandbox denials:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;log show --last 30m --style compact --predicate &lt;span class="s1"&gt;&amp;#39;process == &amp;#34;MyFS&amp;#34;&amp;#39;&lt;/span&gt; 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; tail -40&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Nothing came back, and the agent told me: &amp;ldquo;No log entries so far.&amp;rdquo; Half a minute later it ran &lt;code&gt;log stream … &amp;gt; /tmp/fs.log 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/code&gt; and read the file. Its first line was:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(eval):log:1: too many arguments&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;zsh has a builtin called &lt;code&gt;log&lt;/code&gt; that lists the logins &lt;code&gt;watch&lt;/code&gt; reports. Builtins win over &lt;code&gt;$PATH&lt;/code&gt;, so &lt;code&gt;log show&lt;/code&gt; never reached &lt;code&gt;/usr/bin/log&lt;/code&gt;, and &lt;code&gt;2&amp;gt;/dev/null&lt;/code&gt; threw away the only sign of it. You can check your own shell:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; zsh -c &lt;span class="s1"&gt;&amp;#39;whence -wa log&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;log: builtin&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;log: command&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With &lt;code&gt;/usr/bin/log&lt;/code&gt;, the denial showed up at once, and the agent corrected its earlier claim. The same error turned up in a different project&amp;rsquo;s session days later.&lt;/p&gt;&#10;&#10;&lt;h2 id="trap-2-cargo-test---exact-with-the-wrong-path-passes"&gt;Trap 2: &lt;code&gt;cargo test --exact&lt;/code&gt; with the wrong path passes &lt;/h2&gt;&#10;&lt;p&gt;The 150-run loop called the test binary like this:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$B&lt;/span&gt; --exact tests::&lt;span class="nv"&gt;$t&lt;/span&gt; -q &amp;gt; /tmp/out_&lt;span class="nv"&gt;$t&lt;/span&gt;.txt 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;f+1&lt;span class="k"&gt;))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With &lt;code&gt;--exact&lt;/code&gt;, the filter has to equal the full test path. The tests live in &lt;code&gt;vfs::tests&lt;/code&gt;, so &lt;code&gt;tests::streamed_read_right_after_a_rename&lt;/code&gt; matched nothing, and libtest treats that as success:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 93 filtered out; finished in 0.00s&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Exit code 0, every time. Without &lt;code&gt;--exact&lt;/code&gt;, the same filter would have matched as a substring. The agent listed the real names with &lt;code&gt;$B --list&lt;/code&gt;, fixed the prefix, and the races then showed up under load.&lt;/p&gt;&#10;&#10;&lt;h2 id="trap-3-an-empty-variable-makes-everything-fail"&gt;Trap 3: an empty variable makes everything fail &lt;/h2&gt;&#10;&lt;p&gt;After the fixes, the agent stress-ran the suite 400 times and looked up the binary first:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;ls -t target/debug/deps/storage_core-* &lt;span class="p"&gt;|&lt;/span&gt; grep -v &lt;span class="s1"&gt;&amp;#39;\.d$&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; head -1&lt;span class="k"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The glob had grown past what &lt;code&gt;ls&lt;/code&gt; accepts:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;(eval):1: argument list too long: ls&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;failed runs: 400 / 400&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;code&gt;$B&lt;/code&gt; was empty. zsh drops an empty unquoted word, so each run executed &lt;code&gt;vfs::tests -q&lt;/code&gt;, got &lt;code&gt;command not found&lt;/code&gt; into a log file nobody read, and counted as a failure. This time the agent saw the error right above the count and called the result bogus.&lt;/p&gt;&#10;&#10;&lt;h2 id="fix"&gt;Fix &lt;/h2&gt;&#10;&lt;p&gt;Make every check prove that it ran before you trust what it says.&lt;/p&gt;&#10;&lt;p&gt;Call &lt;code&gt;/usr/bin/log&lt;/code&gt; by its full path, or put &lt;code&gt;command&lt;/code&gt; in front of it, and keep stderr visible while you are still debugging:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/usr/bin/log show --last 30m --style compact --predicate &lt;span class="s1"&gt;&amp;#39;process == &amp;#34;MyFS&amp;#34;&amp;#39;&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; tail -40&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Count the tests that ran, not only the failures. One test per run should say &lt;code&gt;1 passed&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$B&lt;/span&gt; --exact vfs::tests::&lt;span class="nv"&gt;$t&lt;/span&gt; -q &amp;gt; /tmp/out_&lt;span class="nv"&gt;$t&lt;/span&gt;.txt 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;f&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;f+1&lt;span class="k"&gt;))&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;grep -q &lt;span class="s1"&gt;&amp;#39;1 passed&amp;#39;&lt;/span&gt; /tmp/out_&lt;span class="nv"&gt;$t&lt;/span&gt;.txt &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;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$t&lt;/span&gt;&lt;span class="s2"&gt; did not run&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; 1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Get the binary path from cargo instead of a glob, and stop if it is empty:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;cargo &lt;span class="nb"&gt;test&lt;/span&gt; -p storage-core --lib --no-run 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; grep -o &lt;span class="s1"&gt;&amp;#39;target/debug/deps/storage_core-[0-9a-f]*&amp;#39;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;: &lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;B&lt;/span&gt;&lt;span class="p"&gt;:?test binary not found&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;For the agent, add one line to its instructions file (&lt;code&gt;CLAUDE.md&lt;/code&gt; or &lt;code&gt;AGENTS.md&lt;/code&gt;):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-markdown" data-lang="markdown"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;When a check finds nothing, passes everything, or fails everything, first show that it ran:&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;how many items it checked, and that a known-bad input fails it.&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&#10;&lt;h2 id="why-it-happens"&gt;Why it happens &lt;/h2&gt;&#10;&lt;p&gt;All three are the same bug: the harness failed, and the failure looked like a result. A loop that counts exit codes can&amp;rsquo;t tell &amp;ldquo;no failures&amp;rdquo; from &amp;ldquo;no tests&amp;rdquo;, and a pipeline that ends in &lt;code&gt;2&amp;gt;/dev/null | tail&lt;/code&gt; can&amp;rsquo;t tell &amp;ldquo;no matches&amp;rdquo; from &amp;ldquo;no command&amp;rdquo;. Agents write exactly these one-liners, in zsh, many times per session, and they report what the last line printed.&lt;/p&gt;&#10;&lt;p&gt;The agent caught two of the three because the number looked wrong: zero out of 150 for known flaky tests, 400 out of 400 for a suite that had just passed. An empty log search looked plausible, so it went to the user as a finding.&lt;/p&gt;&#10;&#10;&lt;h2 id="takeaway"&gt;Takeaway &lt;/h2&gt;&#10;&lt;p&gt;A clean result from a check you haven&amp;rsquo;t seen fail is not evidence. Before you believe &amp;ldquo;nothing found&amp;rdquo; or &amp;ldquo;0 failed&amp;rdquo;, look for the count of what was checked.&lt;/p&gt;&#10;&lt;p&gt;Next time your agent reports zero failures, ask it how many tests actually ran.&lt;/p&gt;&#10;</description></item></channel></rss>