Run outcomes
Every run of every watch ends with exactly one outcome, recorded in the watch’s run history. Outcomes are the fastest diagnostic in the system: they say which stage the run reached and why it stopped.
| Outcome | What happened | What was stored | What to do |
|---|---|---|---|
disabled | The watch is switched off, so nothing was collected or evaluated. | Unchanged, apart from the run record. | Enable the watch if you expected it to run. |
no-candidates | Sources ran without error but returned no items at all. | Unchanged. | Check the source still returns content — an empty feed, an empty search, or a page that extracted nothing. |
collection-failed | Every source that was tried threw an error. | Unchanged, and the last-run time is deliberately NOT recorded, so the watch stays due. | Look at the recorded source errors. A health alert is also sent for this outcome. |
nothing-new | Items were collected, but every one had already been seen on a previous run. | Unchanged. | Nothing. This is the normal outcome on a quiet day and costs no model calls. |
not-triggered | Fresh content was evaluated and the model decided the condition was not met. | The fresh content is marked as seen so it is never re-evaluated. | If you disagree, the condition or the sources are usually the problem, not the threshold. |
below-threshold | The model triggered, but the result did not clear the bar — either low confidence, or a quality check failed and there was no weak tier to fall back to. | Fresh content marked as seen. | Check the run record: an ungrounded quote or an ungraded source demotes an otherwise confident verdict. |
already-notified | The triggered event has the same identity as one already alerted on. | Fresh content marked as seen. | Nothing. Deduplication working as intended. |
semantic-duplicate | The event is a reworded version of one recently alerted on (embedding similarity at or above 0.92). | Fresh content marked as seen. | Nothing, unless you believe it was genuinely a different event. |
notified | An alert fired and was delivered on at least one channel. | Seen content, the event fingerprint, the event embedding, and the event ledger are all advanced. | Read the alert. Optionally label it to feed the accuracy dashboard. |
notify-failed | The alert was real and passed every check, but every configured channel failed to deliver it. | The formatted alert is stored in a bounded pending-delivery queue and replayed on the next run — no model call is re-spent. | Fix the channel. Delivery is retried automatically for up to five attempts. |
Reading them as a sequence
Section titled “Reading them as a sequence”The outcomes map directly onto the stages of a run, in order:
collection-failed / no-candidates ← stage 1, collectnothing-new ← stage 2, keep only new contentnot-triggered ← stage 3, evaluatealready-notified ← stage 5, exact deduplicationbelow-threshold ← stage 5, tieringsemantic-duplicate ← stage 5, semantic deduplicationnotified / notify-failed ← stage 6, deliverdisabled ← never entered the loopSo an outcome tells you not just what happened, but how far the run got before it stopped.
The three you should act on
Section titled “The three you should act on”The ones that mean “working as intended”
Section titled “The ones that mean “working as intended””nothing-new on most runs is the healthy steady state. already-notified and
semantic-duplicate mean deduplication earned its keep. not-triggered means
the model read new content and correctly found nothing.
Where to see them
Section titled “Where to see them”- Panel / app — a watch’s run history, newest first, with reasoning.
- API —
GET /watches/:id/runs. - CLI —
pnpm watches history <id>. - Scheduled runs — one line per watch in the job log.