Skip to content

Freshness: know when a refresh mechanism has stopped - #15

Draft
aurph wants to merge 1 commit into
mainfrom
feat/freshness-monitor
Draft

Freshness: know when a refresh mechanism has stopped#15
aurph wants to merge 1 commit into
mainfrom
feat/freshness-monitor

Conversation

@aurph

@aurph aurph commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Why

interconnection-queue.json was 77 days old and nothing anywhere knew.

That is not a data problem, it is a missing sense. An unattended pipeline cannot tell you it stopped running. A flow on the Jetson dies quietly when the box reboots, n8n is switched off, or a PAT expires. Nothing errors, because nothing watches. The only symptom is a date that stops moving, and by then it has been months.

What it does against real data right now

GET /api/admin/freshness returns 503 today:

dataset as of age status why
clusters 2026-06-26 40d stale n8n cluster-refresh never mounted
interconnection-queue 2026-05-20 77d stale scan-news-now never scheduled
gpu-rental-prices 2026-06-27 39d stale n8n gpu-price-refresh never mounted
gpu-price-history 2026-08-03 2d ok traffic-driven recorder actually works
hyperscaler-capex / inference-prices / frontier-models manual hand-curated, never alarms
datacenters none none unknown bare array, no date field anywhere

Every stale row is a flow that was built and shipped to main but never mounted. The monitor does not fix staleness. It makes staleness loud.

Design

  • server/freshness-registry.ts declares each dataset: file, timestamp strategy, max age, and the mechanism responsible. That expectation previously lived only in ops/n8n/README.md and in my head. Adding a dataset here is the whole integration.
  • server/freshness.ts classifies. Pure and injected like indices.ts / clusters.ts, so clock and IO stay in the route layer.
  • GET /api/admin/freshness full report. GET /api/admin/freshness/check is the deadman: 200 clean, 503 naming offenders and the mechanism to restart.

Both admin-gated, both added to the PROTECTED_ROUTES table in auth-boundary.test.ts. Freshness is the floor, not a feature; a public "how current are we" page advertises the bare minimum.

The watchdog runs off the Jetson, on purpose

A watchdog hosted on the box it watches dies with it, which is exactly the failure this catches. Point cron-job.org (already firing the daily tweet, alerts on any non-2xx) at the check endpoint with an x-admin-key header. Setup in ops/freshness-monitor.md.

Path is /freshness/check, never /health, so the platform cannot mistake a stale-data 503 for an unhealthy instance and start recycling it.

aging is deliberately a 200

One missed run should not page anyone or the alert stops meaning anything within a month. Only past 2x cadence trips it. unknown does not alarm either: it marks an instrumentation gap, not evidence of failure.

Two supporting honesty fixes

  • The news scanner now stamps lastChecked on every run, and lastRefreshed only on a real change. Staleness follows lastChecked. Otherwise a dataset checked daily and correctly unchanged reads as abandoned, and you cannot tell that apart from the scanner never running at all. Consequence worth knowing: once the scan is scheduled, the monitor can be green while the homepage still shows an older date on that stat. Both are honest, they answer different questions.
  • Dates are read only from fields the file carries, never file mtime, which would report a fresh checkout as fresh data. A bare date parses as the start of its UTC day so nothing reads fresher than it can prove.

Verification

tsc clean, 307 tests pass (287 baseline + 20 new), build passes.

Verified live end to end, not just unit tested:

  • 401 without a key
  • 503 against real data, naming all three stale datasets
  • 200 after temporarily stamping the three files current, so the alarm is proven to go green rather than being permanently red

Not in this pass

The deals n8n flow, the datacenters.json durability backup, and stamping the datacenter ingester so that dataset can report at all. Those are the obvious next steps.

The interconnection queue was 77 days old and nothing anywhere knew. That is
not a data problem, it is a missing sense: an unattended pipeline cannot tell
you it stopped running. A flow on the Jetson dies quietly when the box reboots,
n8n is switched off, or a PAT expires. Nothing errors, because nothing watches.
The only symptom is a date that stops moving.

server/freshness-registry.ts declares every dataset: file, how to read its
timestamp, how old it may get, and what is supposed to refresh it. That
expectation previously lived only in ops/n8n/README.md and in my head. Adding a
dataset here is the whole integration.

server/freshness.ts classifies. Pure and injected like indices.ts and
clusters.ts, so the clock and the IO stay in the route layer:
- ok: within cadence
- aging: overdue but under 2x, which is one missed run
- stale: past 2x, the mechanism has probably stopped
- manual: hand-curated on no schedule, reported but never alarms
- unknown: no readable timestamp, an instrumentation gap not a failure

GET /api/admin/freshness returns the report. GET /api/admin/freshness/check is
the deadman: 200 when nothing is stale, 503 naming the offenders and the exact
mechanism to restart. Both admin-gated and both added to the auth-boundary
route table. Freshness is the floor, not a feature, and publishing a page about
how current we are would be advertising the bare minimum.

The watchdog is pointed at deliberately from off the Jetson (cron-job.org,
which already fires the daily tweet and alerts on non-2xx). A watchdog hosted
on the box it watches dies with it, which is the failure this exists to catch.
Setup in ops/freshness-monitor.md.

Two supporting honesty fixes:
- The news scanner now stamps lastChecked on every run and lastRefreshed only
  on a real change. Staleness follows lastChecked, otherwise a dataset checked
  daily and correctly unchanged reads as abandoned and you cannot tell that
  apart from the scanner never running.
- Dates are read only from fields the file actually carries. Never file mtime,
  which would report a fresh checkout as fresh data. A bare date parses as the
  start of its UTC day, so nothing is ever reported fresher than it can prove.

Against real data today it returns 503 on clusters (40d), interconnection-queue
(77d), and gpu-rental-prices (39d), all stale because their flows were built
but never mounted. datacenters.json reports unknown: it is a bare array with no
date field, so its age genuinely cannot be checked.

tsc clean, 307 tests pass, build passes. Verified live end to end: 401 without
a key, 503 with real data, and 200 after temporarily stamping the three files
current, so the alarm is proven to go green and not just stay red.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant