Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

watchpost

Catches automations that are fresh on the outside, stale inside.

Your monitoring checks whether the job ran. watchpost checks whether it produced — because the expensive failures don't throw errors. The backup writes a file: empty. The report arrives on time: with last week's numbers. The sync says done: having synced nothing.

Born from a real incident

2026-08-03, 16:52. A scheduled market digest on our own machine fired on time, wrote its output file, and passed every freshness check we had — while serving Friday's data under Monday's timestamp. An AI summary layer then narrated three-day-old prices in the present tense, confidently. Every monitor said healthy, because every monitor judged the file, not the content.

watchpost was written the same afternoon, and its first production run caught the incident's residue: the "fixed" digest had been refreshed to stdout only, while the file on disk still held the stale data. The tool flagged its own author's fix as incomplete. That is the job.

The four checks

Check What it catches Who else does it
MISSING / EMPTY The output doesn't exist, or is 0 bytes — the backup that has "succeeded" nightly for months everyone
STALE-FILE Not written recently enough (weekend-aware, so Mondays don't cry wolf) everyone
STALE-CONTENT The newest date inside the file lags the write time — fresh timestamp over old data nobody
FROZEN Rewritten byte-identical N runs straight — a report nobody is feeding nobody

Usage

pip install .          # stdlib only, no dependencies
watchpost watch.json                    # check everything, write the report
watchpost watch.json --out report.html  # self-contained, client-sendable

watch.json:

{
  "client": "Acme Co",
  "watches": [
    {"name": "nightly backup",  "path": "backups/latest.sql", "max_age_hours": 26, "expect_dates": false},
    {"name": "weekly report",   "path": "reports/weekly.txt",  "max_age_hours": 170},
    {"name": "export feed",     "path": "out/feed.csv"}
  ]
}

Exit code is the worst result (0 OK, 1 warnings, 2 failures) so a scheduler can gate on it. The HTML report is self-contained — no external requests — and written to be forwarded to a non-technical owner as-is.

Honest limitations

  • Date recognition is ISO (2026-08-03) and US (08/03/2026) only. Two-digit years are deliberately ignored: 03/08/26 is ambiguous three ways, and a guard that guesses manufactures exactly the false confidence it exists to kill. Files without recognizable dates should set "expect_dates": false and rely on the other three checks.
  • FROZEN needs the state file (--state) to persist between runs.
  • It watches outputs on a filesystem. A stack living entirely inside SaaS tools with no exportable output is not a fit.

Design rules it follows

  • Fail closed, loudly. A malformed watch entry produces a FAIL result, never a silent skip — a guard that crashes on one entry and drops the rest is the failure mode this tool exists to end.
  • Don't cry wolf. Weekend grace on file age, business-day arithmetic on content lag, and deliberately narrow date parsing. A guard that false-alarms every Monday is muted by the second week, and a muted guard is worse than none because it looks like coverage.

Sister project: flatline finds what's already quietly dead (entropy-dead signals, silent no-op jobs, unreachable code). watchpost is the standing guard you leave running after the audit.

Why this repo has two unrelated branches

full-history (the default) holds the commits this tool was actually built in. master holds the single squashed snapshot it was first published as. They share no common ancestor, which looks odd enough to be worth explaining rather than leaving you to wonder: the original release was pushed from a staging directory and carried no history at all — one commit, no failures, nothing to inspect. For a tool built to catch software that reports success while doing nothing, handing you an unexaminable repo was the wrong move.

Rather than force-push over the snapshot, the real history was added alongside it and made the default. Nothing was destroyed; master is still exactly what it was. One file is absent from full-history on purpose: watch-own-stack.json, the manifest describing this machine's own jobs. It names real local paths, so it was stripped from every commit before publishing. demo-report.html only ever existed in the published snapshot and was carried across, so republishing took nothing away either.

The other four

Five small tools, one idea: software reports success while doing nothing, and nobody notices for months. Each answers a different question, and each says plainly what it cannot see.

tool the question it answers its blind spot
attest did this job run, and did it produce what it claimed? it sees declared outputs, not whether they are correct
flatline is this data still carrying information? it waits to be asked
canary what is wrong in the file that just landed? it never sees whether a job ran at all
custody what did the AI actually do, and was it right? it cannot see inside the model, and never claims an answer was true

They share one hash chain and one signature implementation, imported rather than copied — two versions of a trust primitive diverge the first time only one gets fixed.

Why there are several of these, and when we will delete one — the rule each tool had to pass to exist, the one overlap that is real, and the date we have committed to settling it.


MIT. Built by Automated Workflow — the same engine behind our automation monitoring service.

About

Catches automations that are fresh outside, stale inside. Monitoring asks whether the job ran; watchpost asks whether it produced. Born from a digest that served Friday's data under Monday's timestamp.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages