JavaEye

On migration day I ran a preflight checklist before sundown, watched the first night without fixing anything, and scheduled two snapshots to catch what the evening jobs could not.

This is a field report from November 2026, based on one migration day. Paths, job names, and timings are a snapshot of my setup, not a stable reference for yours.

On 9 November, the machine that runs my family’s scheduled jobs changed. The old Mac (an M1) had been the “brain” for months; the new one (an Apple M6, arm64) took over the same evening. Eight jobs were scheduled to fire overnight. This post is the checklist I ran before sundown, the one rule I held all night, and the first real finding — which I deliberately did not fix.

The checklist ran before sundown, not after the first failure

A migration that goes wrong fails at 02:30, not at 20:00. So the work happened in daylight, in three passes.

The night’s roster, for the record: a kanban failure watcher every fifteen minutes; an hourly model-usage watch that reports over chat; two deprecated drain jobs at 22:15; a family digest at 22:50; a night audit at 22:55; a token-ledger aggregation at 23:50; and two backup scripts at 02:10 and 02:30. Everything after 21:00 is unattended. That roster is what scoped the checklist — six scripts, four directories, one status column.

Pass one: the scripts the night’s jobs actually call. Not the whole scripts directory — the six files tonight’s jobs reference. All six existed on the new machine because they had been rsync’d over with the rest of the ~/.hermes/scripts/ tree (72 files total). I checked existence, not correctness. A script that rsync’d is almost certainly intact; a script that did not rsync fails at 02:30 with No such file or directory and nobody awake to read it.

Pass two: the output directories. Jobs that cannot write their output do not always fail loudly — some log a warning and continue, which means the morning report is silently empty. I verified four landing spots by hand: the digests folder, the token-ledger directory, the per-job cron/output/ tree, and the usage log file. All present.

Pass three: job health from the scheduler itself. Each scheduled job has a last_status; every one that had already run on the new machine during the day showed ok, and the scheduler gateway reported running. That is the cheap signal most people skip: the scheduler’s own record beats my memory of what “should” work.

A stopped job still reports, and that is on purpose

Two of tonight’s jobs are deprecated — the processes they used to drain were shut down days ago. The jobs still fire on schedule and still report “nothing to do.”

That looks like waste. It is actually instrumentation. When a job’s only output is silence, you cannot tell the difference between “nothing happened” and “the job is broken.” A report that says ran, found nothing, exiting is a heartbeat. I have written before about how unattended automation fails silently, and how the absence of an error is not evidence of success (scheduled agents outlive the terminal, and so do their failures). Deprecated-but-reporting jobs are the cheap version of the fix: keep the heartbeat, delete the workload later, in daylight.

The first night found a real bug, and I did not fix it

By 21:00 the checklist was green and the rule for the night was set: observe, record, fix nothing. The reasoning is that a fresh machine produces a burst of small failures, and fixing them at midnight produces unreviewed changes on a new system — the worst combination for next-morning debugging. Errors get written down and handled the next day, after review.

The rule paid off within the first hour of planning, not even of running. One of tonight’s jobs — an hourly usage watch — delivers its result as a chat message every hour, all night. The household rule is no automated messages between 21:00 and 08:00. On the old machine this had apparently gone unnoticed or uncommented; on the new one it is a visible defect in the plan. Options are straightforward (suppress at night, or fold it into a daily digest), but the choice belongs in the daylight review, not in a 23:40 cron edit. Recorded, not fixed.

Two snapshots catch what one cannot

The night’s plan ends with two scheduled snapshots — one at 23:59, one at 07:30 — each appending its observations to the same log file. One snapshot after midnight would verify the late jobs but miss the 02:10 and 02:30 backups; one in the morning would see the backups’ outputs but lose the context of the evening. Two cheap, fixed-time write-ins beat one smart check that I would have to remember to run while tired.

The observation file itself is plain markdown with two empty sections waiting to be filled. No dashboard, no database. The format matters less than the habit: migration day produces a written record by construction, not by discipline.

What I would tell myself next migration

Run the checklist in daylight, and scope it to what tonight actually calls — six scripts, four directories, one status column. Keep the deprecated jobs reporting until the new machine has a full quiet week. Set the no-fixing rule before the first failure arrives, because at midnight every failure feels urgent and none of them are. And if a job violates a standing rule, write it down and let the morning decide — a migration that touches twenty things at night is just a more expensive way to not know what broke.