<?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: EL E</title>
    <description>The latest articles on DEV Community by EL E (@el_e_3dc94dfc336bfb025ef2).</description>
    <link>https://dev.to/el_e_3dc94dfc336bfb025ef2</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%2F4108909%2F106b50e5-6589-4505-b434-7d0439d77d1e.png</url>
      <title>DEV Community: EL E</title>
      <link>https://dev.to/el_e_3dc94dfc336bfb025ef2</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/el_e_3dc94dfc336bfb025ef2"/>
    <language>en</language>
    <item>
      <title>A Command That Matched Zero Files Still Exits 0: How to Catch Silent CI Failures</title>
      <dc:creator>EL E</dc:creator>
      <pubDate>Mon, 05 Oct 2026 10:25:15 +0000</pubDate>
      <link>https://dev.to/el_e_3dc94dfc336bfb025ef2/a-command-that-matched-zero-files-still-exits-0-how-to-catch-silent-ci-failures-5cc6</link>
      <guid>https://dev.to/el_e_3dc94dfc336bfb025ef2/a-command-that-matched-zero-files-still-exits-0-how-to-catch-silent-ci-failures-5cc6</guid>
      <description>&lt;h1&gt;
  
  
  A Command That Matched Zero Files Still Exits 0: How to Catch Silent CI Failures
&lt;/h1&gt;

&lt;p&gt;Every engineer has felt the cold dread of pushing a critical fix, watching the CI pipeline turn gloriously green, and then discovering in production that the deployment script didn't touch a single file because a glob pattern quietly evaluated to nothing. &lt;/p&gt;

&lt;p&gt;The pipeline passed. The logs looked clean. But your code was never updated.&lt;/p&gt;

&lt;p&gt;Why does this happen? In standard shell scripting and CI environments like GitHub Actions, commands like &lt;code&gt;grep&lt;/code&gt;, &lt;code&gt;find&lt;/code&gt;, or custom glob expansions frequently exit with status &lt;code&gt;0&lt;/code&gt; even when they match zero files or process zero items. To a build system, "no errors encountered while running the binary" is indistinguishable from "successfully processed your target files."&lt;/p&gt;

&lt;p&gt;In this article, we examine why silent zero-match failures are a silent killer of CI reliability, and how you can bulletproof your automation pipelines using strict assertion wrappers and open-source verification tools.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Root of the Problem: Exit Code Ambiguity
&lt;/h2&gt;

&lt;p&gt;Let's look at a typical cleanup or linting step in a CI pipeline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;find src/ &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.legacy.js"&lt;/span&gt; &lt;span class="nt"&gt;-delete&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;src/&lt;/code&gt; contains no files matching &lt;code&gt;*.legacy.js&lt;/code&gt;, what does &lt;code&gt;find&lt;/code&gt; return? &lt;br&gt;
It exits with &lt;code&gt;0&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;To the shell, &lt;code&gt;find&lt;/code&gt; executed successfully: it searched the directory, encountered no permission errors or syntax issues, and completed its execution. It has no concept of your &lt;em&gt;intent&lt;/em&gt; that matching files &lt;em&gt;ought&lt;/em&gt; to exist. &lt;/p&gt;

&lt;p&gt;Now scale this to a deployment workflow:&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;sed&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="s1"&gt;'s/OLD_VERSION/NEW_VERSION/g'&lt;/span&gt; dist/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.bundle.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your build step changed the output directory structure or filename extensions, &lt;code&gt;dist/**/*.bundle.js&lt;/code&gt; might expand to a literal string or match nothing. &lt;code&gt;sed&lt;/code&gt; might fail or do nothing, but depending on how the shell handles globbing (especially with &lt;code&gt;nullglob&lt;/code&gt; disabled), the command might either fail or silently succeed with zero modifications. If your CI script uses &lt;code&gt;set -e&lt;/code&gt;, it only catches non-zero exit codes. A zero exit code gives your pipeline a false sense of security.&lt;/p&gt;




&lt;h2&gt;
  
  
  Real-World Scenarios Where Zero-Match Slips Through
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. File Migration and Refactoring Scripts
&lt;/h3&gt;

&lt;p&gt;During large-scale refactors, paths move around. If a CI maintenance script relies on relative paths to clean up temporary artifacts or migrate configuration files, a broken path changes the target from "all configuration files" to "nothing at all." &lt;/p&gt;

&lt;h3&gt;
  
  
  2. Security Audits and Secret Scanning
&lt;/h3&gt;

&lt;p&gt;Imagine running a custom pre-commit hook or CI check to ensure no hardcoded private keys or tokens slipped into staging directories:&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-rnw&lt;/span&gt; &lt;span class="s1"&gt;'configs/'&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s1"&gt;'BEGIN PRIVATE KEY'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If &lt;code&gt;configs/&lt;/code&gt; was accidentally excluded or renamed in a recent PR, &lt;code&gt;grep&lt;/code&gt; finds nothing and exits with &lt;code&gt;1&lt;/code&gt; (no match found). But what if you wrappered or piped it incorrectly, or if the directory path was subtly wrong? In many complex pipeline scripts, exit codes from greps are swallowed by pipes or conditional wrappers (&lt;code&gt;|| true&lt;/code&gt;), turning an intended security gate into a ghost barrier.&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Fix It: Defensive Shell Scripting
&lt;/h2&gt;

&lt;p&gt;To stop silent zero-match failures, you must enforce strict assertions on file operations before and after execution.&lt;/p&gt;

&lt;h3&gt;
  
  
  Approach 1: Pre-Check File Counts
&lt;/h3&gt;

&lt;p&gt;Before running a batch operation, assert that your file list or glob expansion actually contains items:&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;#!/usr/bin/env bash&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-euo&lt;/span&gt; pipefail

&lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt; src/&lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;.ts &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="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &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="o"&gt;!&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[0]&lt;/span&gt;&lt;span class="k"&gt;}&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;"Error: Expected TypeScript files to process, but found zero matches."&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&amp;amp;2
  &lt;span class="nb"&gt;exit &lt;/span&gt;1
&lt;span class="k"&gt;fi

&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Processing &lt;/span&gt;&lt;span class="k"&gt;${#&lt;/span&gt;&lt;span class="nv"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[@]&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; files..."&lt;/span&gt;
&lt;span class="c"&gt;# Proceed with processing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Approach 2: Using Dedicated Verification Tools
&lt;/h3&gt;

&lt;p&gt;For robust production pipelines, writing custom bash guards for every glob pattern becomes tedious and error-prone. This is why teams adopt specialized verification wrappers like &lt;code&gt;zero-match-guard&lt;/code&gt; or &lt;code&gt;offchain-integrity-verifier&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;For instance, using &lt;code&gt;zero-match-guard&lt;/code&gt; in your CI workflow ensures that any file operation or glob expansion explicitly fails the build if the match count falls below an expected threshold:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Verify Migration Targets&lt;/span&gt;
  &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;elwakeupman-shhh/zero-match-guard@v1&lt;/span&gt;
  &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;dist/**/*.bundle.js"&lt;/span&gt;
    &lt;span class="na"&gt;min-matches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If zero files match the pattern, the action halts immediately with a clear diagnostic message, preventing broken artifacts from reaching downstream deployment stages. Similarly, for validating build integrity without heavy external dependencies, lightweight verifiers like &lt;code&gt;offchain-integrity-verifier&lt;/code&gt; verify checksums and file presence locally in a single zero-dependency pass.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Silent CI failures are insidious because they rob you of visibility when things go wrong. By recognizing that exit code &lt;code&gt;0&lt;/code&gt; only means "no command execution error" rather than "operation successfully acted on your expected targets," you can write significantly more resilient automation scripts.&lt;/p&gt;

&lt;p&gt;Take a look at your CI pipelines today: are your file-processing steps protected against zero-match silent passes? Add explicit count checks or integrate guard actions like &lt;a href="https://github.com/elwakeupman-shhh/zero-match-guard" rel="noopener noreferrer"&gt;zero-match-guard&lt;/a&gt; to ensure your builds never lie to you.&lt;/p&gt;

</description>
      <category>ci</category>
      <category>bash</category>
      <category>devops</category>
      <category>automation</category>
    </item>
    <item>
      <title>Your ops dashboard does not need a frontend framework</title>
      <dc:creator>EL E</dc:creator>
      <pubDate>Mon, 05 Oct 2026 01:47:01 +0000</pubDate>
      <link>https://dev.to/el_e_3dc94dfc336bfb025ef2/your-ops-dashboard-does-not-need-a-frontend-framework-1e9l</link>
      <guid>https://dev.to/el_e_3dc94dfc336bfb025ef2/your-ops-dashboard-does-not-need-a-frontend-framework-1e9l</guid>
      <description>&lt;p&gt;The dashboard I actually look at every morning is one HTML file with a &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; tag&lt;br&gt;
and no build step. It polls three JSON files on disk and renders a table. It has been&lt;br&gt;
running unattended for weeks.&lt;/p&gt;

&lt;p&gt;I want to argue for that shape, because the default reflex — spin up a framework,&lt;br&gt;
a bundler, a component library, a dev server — costs more than it returns for this&lt;br&gt;
specific category of page.&lt;/p&gt;
&lt;h2&gt;
  
  
  What an internal dashboard actually is
&lt;/h2&gt;

&lt;p&gt;Strip away the ceremony and an ops dashboard is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read some state that another process already wrote down.&lt;/li&gt;
&lt;li&gt;Show it.&lt;/li&gt;
&lt;li&gt;Do it again in fifteen seconds.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;There is no routing. There is usually one viewer. There is no SEO, no SSR, no&lt;br&gt;
hydration, no design system. The interaction budget is "click a link, maybe sort a&lt;br&gt;
column".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alerts&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;status.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;targets.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;alerts.json&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;f&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;?t=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;())));&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;targets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;alerts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tick&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;15000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the whole architecture. The hard parts of this page were never rendering.&lt;/p&gt;

&lt;h2&gt;
  
  
  The hard parts are elsewhere
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Freshness.&lt;/strong&gt; A dashboard that silently shows yesterday's numbers is worse than a&lt;br&gt;
blank page, because a blank page makes you go and check. Mine prints the generation&lt;br&gt;
timestamp of every source file next to the data, and marks anything older than its&lt;br&gt;
expected interval. The number is never shown without saying when it was true.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Saying "unknown" instead of zero.&lt;/strong&gt; If a source file fails to parse, the honest&lt;br&gt;
render is &lt;strong&gt;unknown&lt;/strong&gt;, not &lt;code&gt;0&lt;/code&gt;. I learned this the expensive way: a JSON file written&lt;br&gt;
by a different tool carried a byte-order mark, &lt;code&gt;json.load&lt;/code&gt; raised, the exception was&lt;br&gt;
swallowed, and the page rendered a beautifully empty table. It looked fine. It was&lt;br&gt;
just empty. (The fix was &lt;code&gt;encoding="utf-8-sig"&lt;/code&gt;. The lesson was that the failure had a&lt;br&gt;
display mode indistinguishable from success.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Counting what you did not check.&lt;/strong&gt; Any panel that says "all green" has to also say&lt;br&gt;
how many things it examined. "12/12 checks passed" and "0 checks found" are wildly&lt;br&gt;
different states that render identically if you only show a colour.&lt;/p&gt;

&lt;p&gt;None of those three are frontend problems. A framework helps with none of them.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you give up
&lt;/h2&gt;

&lt;p&gt;Honestly: components, hot reload, type checking across the template boundary, and the&lt;br&gt;
ability to hand the page to someone who only writes React.&lt;/p&gt;

&lt;p&gt;For a page with four panels and one reader, I have not missed any of them. At around&lt;br&gt;
a dozen panels with real interaction I would reconsider — but I would reconsider by&lt;br&gt;
adding one small library, not by starting a project.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that actually matters
&lt;/h2&gt;

&lt;p&gt;Whatever you build it with, the dashboard's job is to be &lt;strong&gt;trusted&lt;/strong&gt;. That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;every number carries its timestamp,&lt;/li&gt;
&lt;li&gt;a source that could not be read says so instead of rendering zero,&lt;/li&gt;
&lt;li&gt;and any "passed" claim states its denominator.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A page that gets those three right in plain HTML is more useful than a beautiful one&lt;br&gt;
that quietly shows stale data — because the first one you can act on, and the second&lt;br&gt;
one you will eventually act on when it is wrong.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build these: single-page operational dashboards, health checks, and the collectors&lt;br&gt;
behind them. No framework, no build step, no monthly service — one file you own and&lt;br&gt;
can read end to end. &lt;a href="https://www.fiverr.com/zerodeptools" rel="noopener noreferrer"&gt;Available for hire on Fiverr&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>devops</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>One command, one file: script in, 1080p episode and a vertical short out</title>
      <dc:creator>EL E</dc:creator>
      <pubDate>Sun, 27 Sep 2026 03:44:40 +0000</pubDate>
      <link>https://dev.to/el_e_3dc94dfc336bfb025ef2/one-command-one-file-script-in-1080p-episode-and-a-vertical-short-out-4c9i</link>
      <guid>https://dev.to/el_e_3dc94dfc336bfb025ef2/one-command-one-file-script-in-1080p-episode-and-a-vertical-short-out-4c9i</guid>
      <description>&lt;p&gt;I run a small storytelling channel. Every episode needs the same five things done in the&lt;br&gt;
same order: clean the script, generate narration, render a 1080p video, cut a vertical&lt;br&gt;
short, and verify the output is what I think it is.&lt;/p&gt;

&lt;p&gt;For a while I did that with a folder of half-remembered ffmpeg incantations. Then I&lt;br&gt;
wrote it down as one script. Now an episode is a single command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python make_episode.py ep05_minxiong.md ep05_minxiong
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the actual output from the last run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1/5] cleaned script: 2096 chars
[2/5] narration: ep05_minxiong_narration.mp3  2,658,384 bytes  443.1s
[3/5] long video:  ep05_minxiong_long_1080p.mp4  6,871,754 bytes / 443.1s
[4/5] short cut:   ep05_minxiong_short_vertical.mp4  892,786 bytes / 58.0s
[5/5] evidence (ffprobe measured, not filename-inferred)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing exotic is happening. The interesting part is what I &lt;em&gt;stopped&lt;/em&gt; doing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 5 is the whole point
&lt;/h2&gt;

&lt;p&gt;The last line of that output says "ffprobe measured, not filename-inferred". That exists&lt;br&gt;
because of a bug that cost me several planning cycles.&lt;/p&gt;

&lt;p&gt;Two files in my working directory were called &lt;code&gt;ep02_..._long_1080p.mp4&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;ep03_..._long_1080p.mp4&lt;/code&gt;. For weeks they appeared in my schedule as full episodes. They&lt;br&gt;
were 80.7 and 78.9 seconds. Nobody had ever measured them. The word &lt;code&gt;long&lt;/code&gt; in the&lt;br&gt;
filename had been doing the job that a measurement should have been doing.&lt;/p&gt;

&lt;p&gt;So the pipeline now ends by running &lt;code&gt;ffprobe&lt;/code&gt; on everything it produced and printing the&lt;br&gt;
real duration and byte count:&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;evidence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;out&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&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;ffprobe&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;-v&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;error&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;-show_entries&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;format=duration&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;-of&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;default=noprint_wrappers=1:nokey=1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="n"&gt;capture_output&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;out&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getsize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is four lines. It is also the only part of the script that has ever caught a real&lt;br&gt;
problem. A pipeline that does not measure its own output is not a pipeline, it is a&lt;br&gt;
hope.&lt;/p&gt;

&lt;h2&gt;
  
  
  The dependency question
&lt;/h2&gt;

&lt;p&gt;The whole thing is standard library plus two external binaries: &lt;code&gt;ffmpeg&lt;/code&gt; and a&lt;br&gt;
text-to-speech CLI. No framework, no orchestration layer, no config format I have to&lt;br&gt;
remember the schema of six months from now.&lt;/p&gt;

&lt;p&gt;That is a deliberate trade. I give up a plugin system I would never have written plugins&lt;br&gt;
for, and in exchange the script still runs on a machine I have not touched since I wrote&lt;br&gt;
it. There is no lockfile to resolve, no transitive dependency that went unmaintained, no&lt;br&gt;
virtualenv that quietly rotted.&lt;/p&gt;

&lt;p&gt;For a tool that runs on a schedule and that nobody is watching, "still runs a year later"&lt;br&gt;
beats "elegant" by a wide margin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a short is generated from the same source
&lt;/h2&gt;

&lt;p&gt;The vertical cut is not a separate project. It comes out of the same render, in the same&lt;br&gt;
run, from the same narration. If I had made it a second workflow, it would have drifted:&lt;br&gt;
different intro, different loudness, different title convention. Two pipelines for the&lt;br&gt;
same content is two pipelines to keep in sync, and one of them always loses.&lt;/p&gt;

&lt;p&gt;One honest limitation, stated plainly: the short is currently the first 58 seconds of the&lt;br&gt;
long cut, not a selected highlight. That is a known weakness and it is on the list. I&lt;br&gt;
would rather ship a mediocre short automatically than a good one manually, because the&lt;br&gt;
manual one does not happen on a week when I am busy.&lt;/p&gt;

&lt;h2&gt;
  
  
  The shape that generalizes
&lt;/h2&gt;

&lt;p&gt;The specific thing here is video. The shape is not:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A repeated task that has an order to it.&lt;/li&gt;
&lt;li&gt;One command that does the whole order.&lt;/li&gt;
&lt;li&gt;A final step that &lt;strong&gt;measures the output and prints the number&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That third step is the one people skip, and it is the one that turns "the script said it&lt;br&gt;
worked" into "the file is 443.1 seconds and 6.8 MB". Those are different claims, and only&lt;br&gt;
one of them is evidence.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I build these pipelines to order — content production, ops automation, scheduled jobs.&lt;br&gt;
Single file, zero third-party Python dependencies, and a final step that proves what it&lt;br&gt;
produced. &lt;a href="https://www.fiverr.com/zerodeptools" rel="noopener noreferrer"&gt;Available for hire on Fiverr&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>ffmpeg</category>
      <category>automation</category>
      <category>devops</category>
    </item>
    <item>
      <title>Your check says PASS. It looked at zero files.</title>
      <dc:creator>EL E</dc:creator>
      <pubDate>Sun, 27 Sep 2026 02:02:23 +0000</pubDate>
      <link>https://dev.to/el_e_3dc94dfc336bfb025ef2/your-check-says-pass-it-looked-at-zero-files-4dji</link>
      <guid>https://dev.to/el_e_3dc94dfc336bfb025ef2/your-check-says-pass-it-looked-at-zero-files-4dji</guid>
      <description>&lt;p&gt;A verification step that finds nothing looks exactly like a verification step that&lt;br&gt;
finds nothing wrong. Both print &lt;code&gt;PASS&lt;/code&gt;. Both exit 0. Only one of them actually did&lt;br&gt;
anything.&lt;/p&gt;

&lt;p&gt;I shipped this bug three times in the same codebase before I understood the shape of it.&lt;/p&gt;
&lt;h2&gt;
  
  
  The bug
&lt;/h2&gt;

&lt;p&gt;A scanner walked a directory and reported leaked secrets:&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;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;glob&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/**/*&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;recursive&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;findings&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;scan&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;files scanned: &lt;/span&gt;&lt;span class="si"&gt;{&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;seen&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | leaks: &lt;/span&gt;&lt;span class="si"&gt;{&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;findings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&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;findings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="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;PASS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It worked. For two months it printed &lt;code&gt;PASS&lt;/code&gt; before every release.&lt;/p&gt;

&lt;p&gt;Then I passed it a list of &lt;em&gt;files&lt;/em&gt; instead of a directory. &lt;code&gt;glob("some/file.mp4/**/*")&lt;/code&gt;&lt;br&gt;
expands to nothing. Zero files scanned. Zero findings. &lt;code&gt;PASS&lt;/code&gt;. Exit 0.&lt;/p&gt;

&lt;p&gt;The caller was the release script. Every release for those two months had been&lt;br&gt;
"verified" by a scanner that had looked at nothing at all.&lt;/p&gt;

&lt;p&gt;Nothing errored. Nothing warned. The log line even told me the truth —&lt;br&gt;
&lt;code&gt;files scanned: 0&lt;/code&gt; — and I had read past it every single time, because the word next&lt;br&gt;
to it was &lt;code&gt;PASS&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why this class of bug survives
&lt;/h2&gt;

&lt;p&gt;Most failures announce themselves. This one impersonates success.&lt;/p&gt;

&lt;p&gt;Three properties make it durable:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The output is indistinguishable from the good case.&lt;/strong&gt; A clean scan and a
scan-of-nothing produce the same word.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It fails open.&lt;/strong&gt; The guard is supposed to &lt;em&gt;block&lt;/em&gt;. When it breaks, it stops
blocking — which is silent by definition.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It gets more trusted over time.&lt;/strong&gt; Every green run is evidence the check works.
Two months of green is two months of accumulating false confidence.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That third one is the dangerous part. The longer it runs, the less anyone looks.&lt;/p&gt;
&lt;h2&gt;
  
  
  The fix is one line of policy, not one line of code
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;scanned&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="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;BLOCK: scanner matched zero files — refusing to report PASS&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;A check that examined nothing must not be allowed to report success.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Note the distinct exit code. &lt;code&gt;1&lt;/code&gt; means "found a problem". &lt;code&gt;3&lt;/code&gt; means "could not do my&lt;br&gt;
job". Those are different states and the caller should be able to tell them apart.&lt;br&gt;
Collapsing them into "non-zero" throws away the one signal that would have caught this.&lt;/p&gt;

&lt;p&gt;While I was there I added two more:&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;if&lt;/span&gt; &lt;span class="n"&gt;missing_paths&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;BLOCK: path does not exist:&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;missing_paths&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&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="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[verify] files scanned: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;scanned&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; | findings: &lt;/span&gt;&lt;span class="si"&gt;{&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;findings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That last line runs on the &lt;strong&gt;success&lt;/strong&gt; path. The happy path now has to state what it&lt;br&gt;
looked at. If the number is zero, you see it at the moment you would otherwise be&lt;br&gt;
reassured.&lt;/p&gt;
&lt;h2&gt;
  
  
  Where else this hides
&lt;/h2&gt;

&lt;p&gt;Once you have the shape, you find it everywhere:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Silent-zero failure&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Test runner&lt;/td&gt;
&lt;td&gt;Pattern matches no tests. "0 passed" is green in several runners.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linter&lt;/td&gt;
&lt;td&gt;Ignore file swallows the whole tree.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backup verify&lt;/td&gt;
&lt;td&gt;Compares a manifest that is itself empty.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grep-based guard in CI&lt;/td&gt;
&lt;td&gt;Typo in the pattern. Nothing matches. Gate opens.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Schema check&lt;/td&gt;
&lt;td&gt;Config fails to parse, exception swallowed, empty dict validates fine.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That last one bit me too, separately: a JSON file written by PowerShell carried a&lt;br&gt;
UTF-8 BOM. &lt;code&gt;json.load(open(path, encoding="utf-8"))&lt;/code&gt; raised, the exception was&lt;br&gt;
caught and turned into &lt;code&gt;None&lt;/code&gt;, and a dashboard rendered an empty table instead of&lt;br&gt;
an error. The table looked fine. It was just empty.&lt;/p&gt;

&lt;p&gt;The fix there was &lt;code&gt;encoding="utf-8-sig"&lt;/code&gt;. The lesson was the same one: &lt;strong&gt;the failure&lt;br&gt;
had a display mode that looked like a working system.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The rule I use now
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;When a check reports success, ask what it examined. If it cannot tell you a number,&lt;br&gt;
it is not evidence.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Concretely, for anything that gates a release, a deploy, or a payment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Print the &lt;strong&gt;denominator&lt;/strong&gt;, not just the verdict.&lt;/li&gt;
&lt;li&gt;Make "examined nothing" a &lt;strong&gt;distinct, failing&lt;/strong&gt; state.&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;separate exit codes&lt;/strong&gt; for "found a problem" and "could not run".&lt;/li&gt;
&lt;li&gt;Write one regression test that feeds the checker an input matching nothing, and
assert that it &lt;strong&gt;fails&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That last test is the one nobody writes. It is also the only one that would have&lt;br&gt;
caught all three of my versions of this bug.&lt;/p&gt;
&lt;h2&gt;
  
  
  I packaged this as a tool
&lt;/h2&gt;

&lt;p&gt;The guard above is four lines, but the rules around it (distinct exit codes, literal&lt;br&gt;
paths that do not exist, counting from the check's own output) turned out to be worth&lt;br&gt;
writing down once. It is one file, standard library only:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/elwakeupman-shhh/zero-match-guard" rel="noopener noreferrer"&gt;https://github.com/elwakeupman-shhh/zero-match-guard&lt;/a&gt;&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;&lt;span class="c"&gt;# blocks instead of passing when the glob matched nothing&lt;/span&gt;
python zero_match_guard.py &lt;span class="nt"&gt;--paths&lt;/span&gt; &lt;span class="s2"&gt;"dist/**/*.js"&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; npm run lint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It ships with the test nobody writes: feed the checker an input matching nothing, and&lt;br&gt;
assert that it fails.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I care about this more than most
&lt;/h2&gt;

&lt;p&gt;Almost everything I build is this kind of tool: a small script that watches something&lt;br&gt;
and tells you whether it is fine. Health checks, integrity verification, log analysis,&lt;br&gt;
scheduled monitors. The entire value of that category is that you stop looking, because&lt;br&gt;
the script is looking for you.&lt;/p&gt;

&lt;p&gt;Which means a monitor that lies is worse than no monitor. No monitor leaves you&lt;br&gt;
uncertain, and uncertainty makes you check. A green dashboard makes you stop checking —&lt;br&gt;
and then you find out two months later.&lt;/p&gt;

&lt;p&gt;So now every one of my checks answers two questions instead of one:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Did you find a problem?&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;How many things did you look at?&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;I build single-file Python tools of exactly this kind: service and disk watchdogs,&lt;br&gt;
integrity verification, log analysis, scheduled monitors. Zero third-party dependencies,&lt;br&gt;
one file, meaningful exit codes — small enough that you can read the whole thing before&lt;br&gt;
you trust it with anything. &lt;a href="https://www.fiverr.com/zerodeptools" rel="noopener noreferrer"&gt;Available for hire on Fiverr&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>testing</category>
      <category>automation</category>
    </item>
    <item>
      <title>Five single-file Python tools I actually run in production ops</title>
      <dc:creator>EL E</dc:creator>
      <pubDate>Sat, 26 Sep 2026 09:47:24 +0000</pubDate>
      <link>https://dev.to/el_e_3dc94dfc336bfb025ef2/5-ge-python-dan-dang-shi-yong-gong-ju-ling-yi-lai-ke-zhi-jie-yong-yu-production-ops-de-zi-dong-hua-jiao-ben-32ej</link>
      <guid>https://dev.to/el_e_3dc94dfc336bfb025ef2/5-ge-python-dan-dang-shi-yong-gong-ju-ling-yi-lai-ke-zhi-jie-yong-yu-production-ops-de-zi-dong-hua-jiao-ben-32ej</guid>
      <description>&lt;p&gt;Over years of doing infrastructure and security work, I kept writing the same&lt;br&gt;
kind of thing: a single-file Python tool, no third-party dependencies, that does one&lt;br&gt;
job well enough to drop into a cron job and forget about. No framework, no install&lt;br&gt;
step, no virtualenv to maintain on a box you only SSH into twice a year.&lt;/p&gt;

&lt;p&gt;Here are five of them, and — more importantly — the specific failure each one exists&lt;br&gt;
to catch.&lt;/p&gt;


&lt;h2&gt;
  
  
  1. Disk and service health probe
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The failure it catches:&lt;/strong&gt; the two boring outages that actually take services down.&lt;br&gt;
A data directory silently fills up until writes and database transactions start&lt;br&gt;
failing, or a background process exits and nobody notices until a customer emails.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; it takes a list of paths and a list of service/process names,&lt;br&gt;
checks free-space percentage against a threshold and whether each process is alive,&lt;br&gt;
prints a Markdown summary, and returns a non-zero exit code if anything breaches.&lt;br&gt;
That exit code is the whole point — it makes the tool composable with any CI or cron&lt;br&gt;
hook without writing glue.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python disk_service_probe.py &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--paths&lt;/span&gt; C:/ D:/ E:/data &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--services&lt;/span&gt; nginx postgres my-worker &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--disk-threshold-percent&lt;/span&gt; 85 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-down-threshold&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. Directory SHA-256 integrity snapshot
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The failure it catches:&lt;/strong&gt; a config directory that drifts. Someone hotfixes a file&lt;br&gt;
on the box during an incident, or a deploy half-applies, and three weeks later nobody&lt;br&gt;
can say which files changed or when.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; walk the tree, hash every file with SHA-256, serialise to a JSON&lt;br&gt;
baseline. Later runs diff current state against that baseline and report added,&lt;br&gt;
removed and modified files. No agent, no database, no daemon — the baseline is just&lt;br&gt;
a file you can commit or archive.&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;# create the baseline&lt;/span&gt;
python dir_snapshot.py snapshot &lt;span class="nt"&gt;--dir&lt;/span&gt; ./configs &lt;span class="nt"&gt;--output&lt;/span&gt; baseline.json

&lt;span class="c"&gt;# compare current state against it&lt;/span&gt;
python dir_snapshot.py verify &lt;span class="nt"&gt;--dir&lt;/span&gt; ./configs &lt;span class="nt"&gt;--snapshot&lt;/span&gt; baseline.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same idea, packaged properly with a hash chain so the record itself is&lt;br&gt;
tamper-evident, is here:&lt;br&gt;
&lt;a href="https://github.com/elwakeupman-shhh/offchain-integrity-verifier" rel="noopener noreferrer"&gt;offchain-integrity-verifier&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  3. USDT receipt verification gate
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The failure it catches:&lt;/strong&gt; trusting a webhook payload, or a screenshot, as proof&lt;br&gt;
that a crypto payment arrived. Both are trivially forged, and by the time you find&lt;br&gt;
out, you have already shipped.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; it is a gate, not a notifier. It checks the claimed transfer&lt;br&gt;
against public on-chain data — recipient address, confirmation depth, amount — and&lt;br&gt;
emits a pass/fail verdict plus a tamper-evident receipt. Read-only: it never holds&lt;br&gt;
a key and never sends a transaction.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python verify_cli.py &lt;span class="nt"&gt;--selftest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Source: &lt;a href="https://github.com/elwakeupman-shhh/usdt-receipt-verifier" rel="noopener noreferrer"&gt;usdt-receipt-verifier&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Offline log analyser
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The failure it catches:&lt;/strong&gt; gigabytes of raw application log sitting on disk doing&lt;br&gt;
nothing, because building a structured summary by hand with grep — error rates,&lt;br&gt;
traffic troughs, repeated anomaly signatures — is tedious enough that nobody does it&lt;br&gt;
until there is already an incident.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; dependency-free parsing of common structured log formats,&lt;br&gt;
frequency histograms bucketed by time, extraction of repeated anomaly signatures,&lt;br&gt;
Markdown report out. Fully offline, so it is safe to point at logs you are not&lt;br&gt;
allowed to ship to a third-party service.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python log_analyser.py /var/log/app/production.log &lt;span class="nt"&gt;--output&lt;/span&gt; report.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  5. Endpoint availability and latency check
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;The failure it catches:&lt;/strong&gt; paying for an external monitoring SaaS to watch a handful&lt;br&gt;
of internal endpoints — which means an account, an agent, an egress path, and alert&lt;br&gt;
noise, for something you could answer in one command from inside the network.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How it works:&lt;/strong&gt; feed it endpoints as plain text, CSV or JSON. It checks HTTP status&lt;br&gt;
and round-trip time (optionally including transfer time), skips private IP ranges by&lt;br&gt;
default as a safety guard, and writes a Markdown report. Any target failing gives you&lt;br&gt;
a non-zero 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;python netcheck.py https://github.com https://cloudflare.com &lt;span class="nt"&gt;--timeout&lt;/span&gt; 5.0 &lt;span class="nt"&gt;--output&lt;/span&gt; netcheck.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  The pattern
&lt;/h2&gt;

&lt;p&gt;Zero dependencies, single file, meaningful exit codes. That combination is what makes&lt;br&gt;
these things survive: they run identically on staging and production, they need no&lt;br&gt;
maintenance window to upgrade, and any of them can be read end to end in one sitting&lt;br&gt;
before you trust it with anything.&lt;/p&gt;

&lt;p&gt;Two of the five are published as proper open-source projects&lt;br&gt;
(&lt;a href="https://github.com/elwakeupman-shhh/usdt-receipt-verifier" rel="noopener noreferrer"&gt;usdt-receipt-verifier&lt;/a&gt;,&lt;br&gt;
&lt;a href="https://github.com/elwakeupman-shhh/offchain-integrity-verifier" rel="noopener noreferrer"&gt;offchain-integrity-verifier&lt;/a&gt;).&lt;br&gt;
The other three are internal utilities — happy to share the approach if any of these&lt;br&gt;
failure modes sound familiar; open an issue on either repo and I will answer there.&lt;/p&gt;




&lt;h2&gt;
  
  
  The tools themselves
&lt;/h2&gt;

&lt;p&gt;All three are one file, standard library only, with a test you can run before you&lt;br&gt;
trust them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/elwakeupman-shhh/zero-match-guard" rel="noopener noreferrer"&gt;zero-match-guard&lt;/a&gt; — fail checks that examined nothing&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/elwakeupman-shhh/usdt-receipt-verifier" rel="noopener noreferrer"&gt;usdt-receipt-verifier&lt;/a&gt; — verify USDT receipts offline&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/elwakeupman-shhh/offchain-integrity-verifier" rel="noopener noreferrer"&gt;offchain-integrity-verifier&lt;/a&gt; — prove a report was not altered&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Available for hire.&lt;/strong&gt; I build this kind of tooling to order: ops automation, integrity&lt;br&gt;
and verification tools, and content pipelines. Single file, zero third-party dependencies,&lt;br&gt;
meaningful exit codes, and a test you can run yourself.&lt;br&gt;
&lt;a href="https://www.fiverr.com/zerodeptools" rel="noopener noreferrer"&gt;zerodeptools on Fiverr&lt;/a&gt;&lt;/p&gt;

</description>
      <category>python</category>
      <category>devops</category>
      <category>automation</category>
      <category>security</category>
    </item>
    <item>
      <title>Stop Trusting Screenshots: Verify Crypto Payments and Prove Your Reports Are Untouched</title>
      <dc:creator>EL E</dc:creator>
      <pubDate>Fri, 04 Sep 2026 02:36:00 +0000</pubDate>
      <link>https://dev.to/el_e_3dc94dfc336bfb025ef2/stop-trusting-screenshots-verify-crypto-payments-and-prove-your-reports-are-untouched-1ja8</link>
      <guid>https://dev.to/el_e_3dc94dfc336bfb025ef2/stop-trusting-screenshots-verify-crypto-payments-and-prove-your-reports-are-untouched-1ja8</guid>
      <description>&lt;p&gt;If you accept crypto (USDT-TRC20) for freelance or digital goods, you've probably been handed a "proof of payment" that was just a screenshot — editable, unverifiable, sometimes a fake TRC20 token.&lt;/p&gt;

&lt;p&gt;Here are two open-source (MIT) tools I use to close that gap:&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Verify the payment actually landed
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;usdt-receipt-verifier&lt;/code&gt; is read-only: give it your address + expected amount + min confirmations, and it checks the chain through a local Tor proxy (no real-IP leak), then writes a tamper-evident receipt. It issues no transaction and never sees your key.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Prove your deliverable report wasn't edited later
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;integrity-chain&lt;/code&gt; turns a report/attestation into a signed hash-chain. An independent verifier re-derives every link and flags tampering or "fake live" claims. Self-test shows honest chains PASS, tampered/fake-live FAIL.&lt;/p&gt;

&lt;p&gt;Both are local-first, zero real IP, no keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it yourself — zero install, offline, no account
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;usdt-receipt-verifier&lt;/strong&gt; — &lt;a href="https://github.com/elwakeupman-shhh/usdt-receipt-verifier" rel="noopener noreferrer"&gt;https://github.com/elwakeupman-shhh/usdt-receipt-verifier&lt;/a&gt;
&lt;code&gt;python usdt_receipt_verifier.py --selftest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;integrity-chain&lt;/strong&gt; — &lt;a href="https://github.com/elwakeupman-shhh/offchain-integrity-verifier" rel="noopener noreferrer"&gt;https://github.com/elwakeupman-shhh/offchain-integrity-verifier&lt;/a&gt;
&lt;code&gt;pip install -r requirements.txt&lt;/code&gt; then &lt;code&gt;python verify_cli.py --selftest&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both exit 0 on honest data and non-zero on tampered/fake-live data — prove it yourself.&lt;/p&gt;

&lt;p&gt;Contact: open a GitHub issue on either repo above.&lt;/p&gt;




&lt;h2&gt;
  
  
  The tools themselves
&lt;/h2&gt;

&lt;p&gt;All three are one file, standard library only, with a test you can run before you&lt;br&gt;
trust them:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/elwakeupman-shhh/zero-match-guard" rel="noopener noreferrer"&gt;zero-match-guard&lt;/a&gt; — fail checks that examined nothing&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/elwakeupman-shhh/usdt-receipt-verifier" rel="noopener noreferrer"&gt;usdt-receipt-verifier&lt;/a&gt; — verify USDT receipts offline&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/elwakeupman-shhh/offchain-integrity-verifier" rel="noopener noreferrer"&gt;offchain-integrity-verifier&lt;/a&gt; — prove a report was not altered&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;strong&gt;Available for hire.&lt;/strong&gt; I build this kind of tooling to order: ops automation, integrity&lt;br&gt;
and verification tools, and content pipelines. Single file, zero third-party dependencies,&lt;br&gt;
meaningful exit codes, and a test you can run yourself.&lt;br&gt;
&lt;a href="https://www.fiverr.com/zerodeptools" rel="noopener noreferrer"&gt;zerodeptools on Fiverr&lt;/a&gt;&lt;/p&gt;

</description>
      <category>crypto</category>
      <category>security</category>
      <category>opensource</category>
      <category>python</category>
    </item>
  </channel>
</rss>
