feat(tomtom_incidents): per-bbox cadence (v0.9.5.1)

Lets each incident bbox poll at its own interval so busy metros refresh more
often than quiet corridors. Backward-compatible, code-only patch.

- Optional BBox.cadence_s (int | None = None) -> per-bbox poll interval; None
  falls back to the adapter's default_cadence_s. Existing settings without the
  field keep their current behavior.
- In-memory _last_polled {bbox_name: datetime}, per process. _bbox_due() gates
  fetches; poll() fetches only due bboxes. First poll after (re)start fetches all
  (one-shot catch-up; storage dedup on <state>:tomtom:<id> collapses overlap).
- _last_polled is recorded ONLY after a successful fetch -- a failed bbox stays
  due and retries next cycle (regression-guarded).
- Supervisor wakes the adapter at the adapter-level cadence; set that to the GCD
  of the per-bbox cadences for exact intervals (extra wakeups cost zero API calls).

central-supervisor restart only. No gui/archive restart, no migration, no new dep.

Full suite: 815 passed, 1 skipped (central and unprivileged zvx, 3x each).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Johnson 2026-05-26 01:01:55 +00:00
commit 7fdf47f2f0
3 changed files with 106 additions and 2 deletions

View file

@ -1533,6 +1533,12 @@ coverage. One event per incident.
larger). Ships with Treasure Valley (Boise). **Cadence 1800s (30 min)** ->
1 bbox = 1,440 calls/mo, 58% of the 2,500/mo free-tier cap. Adding bboxes must
respect `N * (43200/cadence_min) <= 2500`.
- **Per-bbox cadence:** each bbox may set an optional `cadence_s` (else falls back
to the adapter's `default_cadence_s`). The supervisor wakes the adapter at the
adapter-level cadence; `poll()` fetches only bboxes whose per-bbox interval has
elapsed (in-memory `_last_polled`, per process; first poll after restart fetches
all). Set the adapter cadence to the GCD of the per-bbox cadences for exact
intervals -- extra wakeups cost zero API calls.
- **Dedup key shape:** `<state_code>:tomtom:<tomtom_id>` (e.g.
`ID:tomtom:TTI-5df75143-...`); the upstream id is stable across polls.
- **Severity:** from `magnitudeOfDelay` (0->1, 1->1, 2->2, 3->3, 4->4; 4 ==