> ## Content Index
> Fetch the complete content index at: https://www.livain.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# The daily automation that never calls a model: local scheduled jobs, a receipt printer, and designing for the device being offline
- URL: https://www.livain.com/blog/local-scheduled-jobs-no-model-receipt-printer-device-offline/
- Published: 2026-10-08T07:00:00.000Z
- Updated: 2026-10-08T06:59:59.000Z
- Description: Claude Code built it; nothing in the running loop calls a model. The useful lesson was not the printer - it was that the output device is the flakiest link, and coupling the email to the print cost me both.
- Author: Remco Livain
- Tags: AI, Claude Code, Automation, Scheduled Tasks, Marketing Ops

The most useful thing I built this month prints my to-do list on a thermal receipt printer at seven thirty in the morning, and it does not call a language model even once while it runs.

I used Claude Code to build it. That part took a few evenings. But the running system is a handful of local scheduled jobs on a Mac mini: no tokens, no API, no network round-trip, no non-determinism anywhere in the hot path. I think that distinction is the single most underrated decision in AI tooling right now, and most of the "AI automation" I see described online gets it backwards.

### What it actually does

A new task appears in Todoist, and within half a minute a small ticket prints with the task, its project and its due date. I tell Siri to add something to the shopping list, and a slip prints — or, if I ask for the whole list, it prints grouped into shop sections, walked in the order I walk the shop, with the grouping guessed from keyword lists in three languages because the Reminders app will not let a script read its own section headings.

At half past seven every morning a brief prints and the same brief arrives by email: the day's calendar, today's and overdue tasks, the weather for two towns, and two clients' numbers from the previous day — revenue and orders for one, enquiries, ad spend and cost per enquiry for the other. Each number carries a comparison against the seven-day average, because a bare figure invites you to invent a trend.

Everything in that paragraph is a [launchd agent](https://alvinalexander.com/mac-os-x/launchd-plist-examples-startinterval-startcalendarinterval/?ref=livain.com) — some on `StartInterval`, polling every thirty seconds, the brief on a calendar interval. The iPhone side is a shortcut that writes a text file into an iCloud folder, which the Mac watches. Low technology, embarrassingly reliable.

### Where the model is, and where it is not

The model wrote the slip renderer, the ESC/POS writer, the section-guessing word lists, the comparison logic and all the plumbing. It also talked me out of one architecture and into a better one. That is real leverage and I am not going to pretend otherwise.

But there is no reason for a model to be in the loop when the job is "fetch tasks due today, lay them out, send bytes to a printer". Putting one there buys you latency, cost, a dependency on somebody else's uptime, and output that varies between runs. Martin Fowler's line is the right one to keep in mind: [all a language model does is produce hallucinations, it is just that we find some of them useful](https://www.martinfowler.com/articles/202508-ai-thoughts.html?ref=livain.com). Useful hallucinations are wonderful for writing code. They are a poor choice for a daily job whose entire value is that it says the same true thing every morning.

> Use the model to build the system. Think twice before you put it inside the system.

The same reasoning sits behind [replacing a SaaS tool with a single HTML file](https://www.livain.com/blog/replaced-loom-with-a-single-html-file-claude-code-buy-vs-build/) and behind [pushing client KPIs onto an e-ink screen instead of building another dashboard](https://www.livain.com/blog/client-kpis-eink-screen-webhook-instead-of-another-dashboard/). In all three cases the AI earned its keep in the build and then got out of the way.

### The physical device is the least reliable component

Which brings me to the part I actually got wrong.

The printer drops off the network. Not often, but properly: once for about thirty seconds, then once from a Saturday evening to a Sunday morning, more than twelve hours. The Mac was healthy each time — right subnet, router answering — and the printer simply stopped replying. A thirty-second TCP check running the whole time did nothing to prevent it.

Here is the failure that matters, though, and it is mine, not the printer's. On the morning the device was gone, the brief did not print, which I expected. The brief also did not arrive by email, which I did not. One function did both things in sequence, the print threw an exception, and the email never got sent. I lost the delivery channel that was working because the one that was broken ran first.

That is a design error with a name in operations. Google's SRE book frames monitoring around [catching symptoms rather than causes](https://sre.google/sre-book/monitoring-distributed-systems/?ref=livain.com), and the symptom here was never "the printer is unreachable" — it was "Remco did not get his brief". I had instrumented the cause and coupled the symptom to it.

### What it looks like after the fix

The email goes out on time regardless, carrying a short note that the printer did not answer and the Mac is still trying. The print then retries every thirty seconds for half an hour and gives up quietly. The watchers already behaved correctly by accident, because they only write their "already printed" state after a successful print, which makes them [naturally idempotent](https://www.livain.com/blog/the-same-task-three-days-running-idempotency/) — a thing I had to learn the hard way elsewhere.

And there is now an availability log that records the Mac's address, whether the router answers, and the printer's ARP state at every transition. Only on transitions. The first version logged on every error-type change and filled a file with "still down", which is the same mistake as a scheduled report that tells you nothing changed: [noise is how a check becomes invisible](https://www.livain.com/blog/i-scheduled-a-weekly-check-that-could-never-run/).

One honest footnote on the printing itself. The raster command I use to push the bitmap, `GS v 0`, is [formally obsolete in Epson's own reference](https://download4.epson.biz/sec%5Fpubs/pos/reference%5Fen/escpos/gs%5Flv%5F0.html?ref=livain.com) — supported by some models, not by future ones. It works on this printer today and I wrote the line in the code that says so, with the replacement function named. That note costs nothing now and saves an hour in two years.

### Why a fractional marketing exec builds a receipt printer

Partly because it is fun. Mostly because the brief is the thing that makes two client mandates legible to me before the day starts, and I would rather spend four evenings owning that than pay for a dashboard I have to go and open.

But the transferable lesson has nothing to do with paper. It is that when you automate into the physical world — a printer, a screen, a till, a door — the output device is the flakiest thing in the chain, and the version of your automation that assumes it is present will fail silently on the day you most want it. Decide now what happens when it is gone.

The printer will be offline again. The brief will arrive anyway.

### Sources & further reading

**External**  
[Some thoughts on LLMs and software development](https://www.martinfowler.com/articles/202508-ai-thoughts.html?ref=livain.com) (Martin Fowler)  
[Monitoring Distributed Systems](https://sre.google/sre-book/monitoring-distributed-systems/?ref=livain.com) (Google SRE Book, chapter 6)  
[launchd plist examples: StartInterval and StartCalendarInterval](https://alvinalexander.com/mac-os-x/launchd-plist-examples-startinterval-startcalendarinterval/?ref=livain.com)  
[GS v 0 (obsolete command)](https://download4.epson.biz/sec%5Fpubs/pos/reference%5Fen/escpos/gs%5Flv%5F0.html?ref=livain.com) — Epson ESC/POS command reference

**Related posts**  
[Client KPIs on an e-ink screen instead of another dashboard](https://www.livain.com/blog/client-kpis-eink-screen-webhook-instead-of-another-dashboard/)  
[I replaced Loom with a single HTML file](https://www.livain.com/blog/replaced-loom-with-a-single-html-file-claude-code-buy-vs-build/)  
[The same task three days running: idempotency](https://www.livain.com/blog/the-same-task-three-days-running-idempotency/)  
[I scheduled a weekly check that could never run](https://www.livain.com/blog/i-scheduled-a-weekly-check-that-could-never-run/)