The first Service Monitor’s build log is retired. Its code isn’t on this machine anymore, so this is a rebuild, not a port — the old entries came down with it, and what stays is the list of things it taught me:
“Unknown” is a state, not a bug. A monitor that can’t see must never say “down” — and a gap in the record must never be smoothed over.
One writer owns the truth. If two things can write, they will disagree at 3 am.
Bounded everything. Queues, retries, bodies, pages — every one gets a limit and a deadline.
So the rebuild starts differently: the contract lives in a pure domain crate (no I/O allowed — a script enforces it), the plan lives in docs/ (nine module docs, each with failure cases and test parameters), and every milestone answers to a roadmap table of outcome / invariant / failure case / acceptance test.
What I built
The scaffold: monitor-domain (monitor types, the outcome model — pass, HTTP fail, transport, timeout are different kinds of evidence — and the incident state machine), monitor-db (SQLite, WAL, one migration), monitor-server (/livez, /readyz, monitor CRUD, serves the dashboard), a monitor CLI, and a SvelteKit + Tailwind + shadcn-svelte dashboard that distinguishes “server unreachable” from “everything is fine”.
Eleven tests, the full gate green, and one honest shell.