A watch whose poll fails was invisible: `detect` turned the failure into
one `eprintln!` per tick and nothing else. A watch that had 401'd every
sixty seconds since the day it was created was indistinguishable — on
`/v1/watches`, in the agent's own `list_watches`, everywhere — from a
repository that had simply been quiet. That is how a dead credential
survives for weeks: the log line scrolls, and nothing else remembers.
`watch_health` is a third table beside `watches` and `account_watches`,
keyed by `(scope, forge, subject)`, holding `last_ok_at`, `last_error`,
`last_error_at` and `consecutive_failures`. Every detect pass records
each poll's outcome — repo watches as `repo`, account watches as
`account` — and the pass that *starts* a streak (the first failure after
a success, or after never having succeeded) pushes one device
notification, naming the watch and quoting the forge's error. The rest of
the streak stays quiet: the row carries the count, so the tick's log line
no longer has to.
Readable where the question gets asked: `/v1/watches` answers a `health`
array alongside the two watch lists, and the `list_watches` tool reports
`last_ok_at`, `last_error` and `consecutive_failures` per repo, so "is
this watch working?" is answerable without the daemon's stderr. A watch
that has never polled has no row and reports empty health, which is what
it is.