Home / Vault & Second Brain / Eighteen Checks, One That Can Fail

Eighteen Checks, One That Can Fail

A row of eighteen faint check panels with only one lit bright green

This morning I did something I try to do about once a week: I counted what my cron jobs actually check — not what they report. The difference turned out to be bigger than I expected, and it is the same difference that has quietly bitten me three times this autumn.

The numbers, measured at 10:00 today across all ten job stores (194 jobs):

  • 18 jobs contain a read-back command — stat -c, git diff, grep -c, sqlite3, hermes cron list.
  • 4 jobs contain a construct that can actually make the run fail — exit 1, grep -q, test -f, set -e, or a checked returncode.
  • 1 job has both in the same prompt.

So seventeen of those eighteen jobs read something back and then print it. A stat -c '%y %s' that ends up in a report is an observation. Nothing in that job compares the observation to an expectation, which means the job cannot reach any conclusion other than success. That is not a gate. That is a mirror.


The one that comes closest — and still cannot fail

My daily platform-top2 job has a step 7 that is literally titled “Gate (bewijs, geen aanname)”. It runs stat -c '%y %s' on the note it was supposed to write, and the prompt says: take mtime and size into your answer, word for word. Then: if the file does not exist or is 0 bytes → first line exactly …

That is the right instinct. It is also prose. There is no || exit 1 behind it, so when the file is missing the model writes one honest sentence about a missing file and the scheduler records status: ok. A warning in the body of a report is invisible to everything that reads reports automatically — which is all of my monitors.

Ten stores, one reader

The second half of today’s measurement is worse, because it is not about a job at all. My skill curator archives skills that have gone unused, with one exception written into the code at agent/curator.py:218: skills referenced by a cron job are in use by definition, so treat them as pinned. Good rule.

But that protection calls referenced_skill_names(), which calls load_jobs() — and load_jobs() reads the store of the profile the curator happens to be running in. Measured today: 18 skill references are visible from the root store, while 27 exist across all ten stores. Nine skills sit in the gap between “referenced by a job” and “protected from archival”, and schrijfstijl-john — the measured style profile behind every personal post I publish — is one of them.

The job that references it — a nightly blog cron in another profile, still enabled — is exactly the case the comment in the code was written for. It was simply never covered. And the failure mode is silent by design: a job whose skill is missing logs a warning and runs anyway, with status: ok.


What I actually changed

Less than I would like, and I want to be honest about that. The skill is active and pinned now, so the curator leaves it alone. The structural fix — make the protection read every store, and make a missing skill a failure instead of a warning — touches a code path outside my own jobs, so it waits for the maintenance window instead of being hot-patched on a Friday morning.

The lesson I can apply today is cheaper anyway. Every check I write from now on gets one question attached: can this line make the run fail? If the answer is no, it is a note, and I should stop calling it a gate in my own report. Eighteen of those is not eighteen guards. It is eighteen sentences that agree with me.

I am curious how others handle this: do you count your checks, or do you trust the status field? Let me know in the comments.

Wat vond je van dit bericht?

Leave a Reply

Your email address will not be published. Required fields are marked *